简体   繁体   English

Rails查询优化(消除n + 1个查询)

[英]Rails query optimization(eliminating n+1 queries)

I am working on a project and there is a complex query that takes 10 seconds or so to perform. 我正在一个项目上,有一个复杂的查询需要花10秒钟左右的时间来执行。 I realize that there is an N + 1 query happening but I am new to rails and I am not sure how to fix it. 我意识到有N + 1查询正在发生,但是我是Rails的新手,我不确定如何解决它。 The controller code is : 控制器代码为:

def index

    filters = params.slice(:package_type, :guid)
    list = packages
    list = list.where(filters) unless filters.empty?

    respond_to do |format|
      format.html { @packages = list.includes(:classification).order(:priority => :asc) }
      format.json { @packages = list.includes(:classification, {channels: [:classification, :genres]}, :extras).order(:priority => :asc) }
    end
  end

the package model has 包装型号有

class Package < ActiveRecord::Base
  extend FriendlyId


  belongs_to :classification
  has_many :package_channels
  has_many :channels, -> { order(:priority => :asc, :identifier => :asc) }, through: :package_channels
  has_many :package_extras
  has_many :extras, -> { order(:identifier => :asc) },through: :package_extras

the channels model has: 渠道模型具有:

class Channel < ActiveRecord::Base

  belongs_to :classification
  has_many :channel_genres
  has_many :genres, through: :channel_genres
  has_many :package_channels
  has_many :packages, through: :package_channels

I also want to mention that filters is usually empty. 我还想提到过滤器通常是空的。 If I am missing any info please feel free to comment and I will add it. 如果我缺少任何信息,请随时发表评论,我会添加它。 Thanks for your time! 谢谢你的时间!

Here is the #packages method from the controller. 这是控制器的#packages方法。

 def packages
    @plan ? @plan.packages : Package
  end

Here is the view: index.json.jbuilder 这是视图:index.json.jbuilder

json.cache! ["cache", "#{params["plan_id"]}_packages_index"] do
  json.array! @packages do |package|
    json.partial! 'packages/package_lean', package: package
  end
end

I do not see the query itself, so I probably wont be able to answer specifically for this case. 我看不到查询本身,因此我可能无法专门针对这种情况进行回答。

1. Use Eager Loading to transform N+1 queries into 1 1.使用急切加载将N + 1个查询转换为1个

In general, your first step should be using eager loading technique to prevent N+1 queries. 通常,第一步应该使用预先加载技术来防止N + 1查询。 Most likely you are requesting associated collection (or single object) that is not yet loaded. 您很可能正在请求尚未加载的关联集合(或单个对象)。

# controller
def index
  @customers = Customer.active
end

# view
<% @customers.each do |c| %>
  <%= c.name %> # this is fine the object is already in memory from your controller
  <%= c.address %> # this one makes a query to the database
<% end %>

This is usually solved by adding includes(association) . 这通常可以通过添加includes(association)来解决。

@customers = Customer.active.includes(:address)

2. Make sure you have an index for association's foreign key 2.确保您有关联的外键索引

Another good thing to have is an index for association's foreign key. 另一件好事是关联外键的索引。

add_index :customer, :address_id

DB engine may choose not to use this index when building a plan of execution for some complex query, but, for a simple one, this is the case. 在为某些复杂的查询建立执行计划时,数据库引擎可能会选择不使用该索引,但是对于一个简单的查询,情况就是如此。

3. Use bullet gem 3.使用子弹宝石

There is a badass gem called bullet . 有一种坏蛋叫做子弹 It will watch your queries while you develop your application and notify you when you should add eager loading (N+1 queries), when you're using eager loading that isn't necessary and when you should use counter cache. 在开发应用程序时,它将监视您的查询,并在您应添加紧急加载(N + 1查询),使用不必要的紧急加载以及何时使用计数器缓存时通知您。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM