简述n+1问题

2018-11-13  本文已影响0人  biubiudog

在Rails ActiveRecord中,常会嵌套遍历很多orm模型。
如:常用的一对多(用户和文章)模型中

    class User
      has_many :articles
    end

    class Article
      belongs_to :user
    end

如果有十个用户,那么查询每个用户对应的文章需要查询11次。

     User.all.map{|user| user.articles}
    select * from users
    select * from articles where user_id = 1
    ...
    select * from articles where user_id = 10

为了解决这个问题,一般的解决方案是:

1. 预加载

使用includes, preload, eager_load

    User.includes(:articles).map{|user| user.acticles }
    select * from users 
    select * from articles where user_id in (1..10)

只需要2条查询语句

includes 和preload 、eager_load的区别

Rails 提供了4种方式来加载关联表的数据:

User.preload(:articles)是默认的User.includes(:articles)加载方式。

加入查询条件时使用preload会报错:

User.preload(:articles).where("articles.content = '123' ")
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: articles.content: SELECT  "users".* FROM "users" WHERE (articles.content = '123' ) LIMIT ?

includes加入查询条件不会报错

User.includes(:articles).where("articles.content = '123' ")

而preload的条件中加入users表的查询条件则没有问题:

User.preload(:articles).where("users.name = '123' ")

2.使用Goldiloader

(参考 https://github.com/salsify/goldiloader
安装 gem 'goldiloader'
默认情况下,所有关联在首次访问时都会自动加载。
你可以手动添加代码auto_include,禁止自动预先加载:
User.auto_include(false)
在关联关系中使用proc来禁止自动预先加载。

      class  User < ActiveRecord::Base
          has_many :articles, ->{ auto_include(false) }
      end     

fully_load选项可用于强制ActiveRecord完全加载关联(并执行任何必要的自动切换加载)

class  Blog <ActiveRecord :: Base 
  has_many :posts,fully_load: true 
end
上一篇下一篇

猜你喜欢

热点阅读