N + 1 Problems
N + 1 is the most common performance problem in database access. First, let's introduce what N + 1 is:
For example, there are two tables in our database: MERs and Orders. Orders contains a foreign key mermer_id pointing to the Customers primary key id.
If you want to obtain all the customers and their corresponding Order, one way is
SELECT * FROM MERs MERS;
For each Customer;
SELECT * FROM Orders WHERE Orders. customer_id = # {customer. id}
In this way, we actually perform N + 1 queries on the database: select all customers to get N customers at a time, and select their corresponding Order for N customers for a total of N times. Therefore, a total of N + 1 queries are executed, which is the N + 1 problem.
General Solution to N + 1 Problems
Use Left Join to retrieve all data at a time:
SELECT * FROM MERs left join Orders on Customers. id = Orders. customer_id
In this way, although the retrieved data is relatively large, it only needs to be executed once.
N + 1 in Rails
Because Rails uses ActiveRecord to access the database. Therefore, its N + 1 problem is not so obvious.
Suppose we have two ActiveRecord: Customer and Order.
Customer has_orders: orders
Order belong_to: customer
Generally, the method for getting all the customers is:
MERs = Customer. all
If we follow
Customers. each {| customer | puts customer. orders. amount}
In this case, the N + 1 problem occurs, because the orders is not obtained when obtaining all the customers. Then, in the subsequent each traversal, the orders will be retrieved one by one, which constitutes N + 1 in rails.
Problem.
Solution to the N + 1 problem in Rails
Customers = Customers. orders des (: orders)
In this way, all orders are retrieved at one time when reading MERs. More detailed descriptions of the references
More
Not all foreign key associations are involved, but the one-to-many problem will produce N + 1, which depends on your business. For example, when your method is executed, only a few users may obtain the orders corresponding to the customer, so you can keep the default lazy method. The failure to force the eager to retrieve data is not worth the candle.
References:
Http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations