如何解决这个N + 1。轨。另外,为什么N + 1查询如此糟糕?

时间:2017-06-08 16:24:37

标签: ruby-on-rails database

所以我只想确保这是一个N + 1查询,我想知道如何修复它。在较高的水平,我在N + 1查询中失去的时间最多?它是通过网络向数据库URL请求最耗费时间吗?或者实际查询 IN 数据库是否花费了我最多的时间?

说我们有这个:

products = Product.where(user_id: user.id)  # This is one network database query right?

products.select { |product| !product.restrictions.map(&:state).includes?(user.address.state) } 
# restriction is another table. We're trying to filter out products that are restricted to the user's state.

问题

  1. 从技术上讲,这是一个N + 1查询吗?这是因为我们通过一个查询来获取用户 AND 另一个产品的所有产品,通过将产品的状态限制与用户进行比较来过滤掉受限制的产品'国家。

  2. 所以,从高层次来看,我该怎么办?我可以在第一次查询中急切加载限制表吗?或者我应该只进行一次旅行并在第一次查询中执行所有操作?这是我的两个选择吗?

  3. 更新

    所以假设我做了Product.includes(:restrictions).where(user_id: user.id),这对所有人来说都是正确的查询吗?

    如果这是一个方法,那么这也是一个查询:

    products = Product.where(user_id: user.id)`
    products.includes(:restrictions).select do |product|
    !product.restrictions.map(&:state_name).include?("CT")
    end
    

2 个答案:

答案 0 :(得分:0)

有一些很棒的文章like this one更详细地介绍了这一点,但一般的概念是,每当你在列表中进行迭代并根据该列表进行查询时,你就会执行N +1查询。您正在通过网络丢失最多的时间,但每个查询都会带来一定的开销。

问题1.是的,这是一个N + 1查询。

当您退回每件商品时,您正在进行N + 1查询,然后针对您返回的每件商品进行是否受限制。您通常可以识别它们,因为您正在使用products.select { |product| }迭代查询。当您可以批量处理时,这可能会产生数百个查询。

在您的示例中,您将返回一系列产品并发出另一个请求来过滤掉列表。

您的代码目前生成的SQL类似于:

SELECT * FROM products WHERE user_id=1; 

然后执行另一个过滤器,您要检查产生这些查询的产品限制:

SELECT restrictions FROM products WHERE product_id=1, user_id=1, state="x";
SELECT restrictions FROM products WHERE product_id=2, user_id=1, state="x";
SELECT restrictions FROM products WHERE product_id=3, user_id=1, state="x";
...

问题2.您应该只在一个查询中执行此操作并批量处理结果。这是伪造的,但你明白了这个想法:

products = Product.where(user_id: @user).select(restrictions: state)`

对您的更新的回复:这仍然会创建两个查询。该方法只是从上到下运行。使其仅创建一个查询的唯一方法是使用并链接提供的Rails ORM方法,这些方法创建代理对象,然后创建单个查询。在此处阅读更多内容:https://stackoverflow.com/a/10747692/1336988

答案 1 :(得分:0)

在@ codelitt的回答之后,以下是你如何解决这个问题。 你应该打电话给

products = Product.includes(:restrictions).where(user_id: user.id) // or you can write the includes in your scope

这将获取内存中产品的所有相关restriction记录。因此,在下一行中,将不会有一堆数据库查询成本高昂。相反,它将从内存中获取记录。