ruby-on-rails 如何修复这个N+1.Rails,还有,为什么N+1查询这么糟糕?

2wnc66cl  于 2023-03-31  发布在  Ruby
关注(0)|答案(3)|浏览(111)

所以我只想确定这是一个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查询吗?这是因为我们正在进行一个查询来获取用户的所有产品**,另一个查询通过比较产品的状态限制和用户的状态来过滤出受限制的产品。
1.那么,从更高的层次上讲,我能做什么呢?我能在第一个查询中立即加载限制表吗?或者我应该只执行一次,然后在第一个查询中完成所有操作?这是我的两个选择吗?

更新

所以假设我做了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
ao218c7q

ao218c7q1#

N+1查询是Rails应用程序中常见的性能问题,在这种情况下,会多次查询数据库以获取相关记录,从而导致响应时间变慢。
重要的是要知道如何识别,修复和避免它在未来。

步骤如下:

  • 确定N+1查询
  • 分析查询
  • 使用急切加载
  • 使用连接
  • 使用counter_cache
  • 测试与测量

通过遵循这些步骤,您可以识别、调试和修复Rails应用程序中的N+1个查询,并提高应用程序的性能。
有一篇很大的文章,您可以在其中找到对这些步骤的详细解释-https://ruby.mobidev.biz/posts/how-to-find-debug-fix-n+1-queries-in-rails/
希望这会有帮助!

bxgwgixi

bxgwgixi2#

在@codelitt的回答之后,这里是你解决这个问题的方法。你应该打电话
products = Product.includes(:restrictions).where(user_id: user.id) // or you can write the includes in your scope
这将获取内存中所有与产品相关的restriction记录。因此,在下一行中,不会有一堆昂贵的db查询。相反,它将从内存中获取记录。

ca1c2owp

ca1c2owp3#

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

相关问题