N + 1 问题的解决方案

Rails中的Active Record的关联功能很强大,但是有时会产生N+1 SQL 查询问题。比如查看一篇文章下的很多评论就会产生N+1 问题。
PS: 问题往往出现在一对多和多对多的关系中。
参考资料: ihower实战圣经

解决思路如下:

1. 安装检测工具

(1)gem 'rack-mini-profiler' ,效能检测工具。安装后,网页上左上角会出现网页加载时间的提示。
(2)gem 'bullet', group: 'development', N+1 query检测工具。使用流程可参考:bulldet,当你的代码存在N+1问题时,会出现例如下面的提示:

1
2
3
4
USE eager loading detected
Post => [:user]
Add to your finder: :includes => [:user]
Call stack

2. 使用 inclueds 关联

(1)model 之间的关系

1
2
3
4
5
6
7
class User < ApplicationRecord
has_many :posts
end

class Post < ApplicationRecord
belongs_to :user
end

(2)修改控制器中代码

1
2
3
4
5
class PostsController < ApplicationController
def index
posts = Post.includes(:user).page(params[:page])
end
end

此时,就会观察到rails log中只会出现两条SQL查询语句,。

1
2
Comment Load (0.5ms)  SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ?  [["post_id", 1]]
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" IN (4, 18, 2, 14, 13, 3, 12)

N + 1 其实属于是慢查询的问题,下面就大体总结下解决慢查询问题及解决方法:

  • N + 1 ——– ——–includes
  • 大量数据 ———– find_each(或者 find_in_batches)
  • 查询慢 ————– 加 索引
  • 捞出所需字段 —— select 方法
  • 计数快取 ———- Counter cache