iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 9
1
Modern Web

關於 Ruby on Rails,我想說的是系列 第 17

[Day 17] 深入了解includes 原理

  • 分享至 

  • xImage
  •  

昨天 [Day 16] 關聯資料表加載,解決 N+1 Query,我們得到了includes這個方法可以解決N+1 query,但是背後的原理是什麼? 就是我今天的主題。

一樣先從資料表的關聯開始...

class Product < ApplicationRecord
  has_many :styles, class_name: 'ProductStyle' # 關聯取名為styles
end
class ProductStyle < ApplicationRecord
  belongs_to :product
end

雖然只是簡單的加上has_many關聯,Rails已經幫我們產生很多黑魔法,包含最常用的關聯方法:styles,只要Product.last.styles 就可以呼叫出最後一個商品的所有款式。
其實:styles方法會先看記憶體cache裡面是不是已經有@association_cache這個實體變數,有的話就直接從@association_cache裡面把資料拉出來,如果沒有才用SQL語法跟DB要資料。

實體變數 @association_cache

來比較是否使用eager_loading(預加載),兩種情況的@association_cache

不使用eager_loading

product = Product.last
product.instance_variable_get(:@association_cache) # => {} 

@association_cache回傳一個空的hash,所以@association_cache預設值是空hash

使用eager_loading

product = Product.includes(:styles).first
product.instance_variable_get(:@association_cache)
# => {:styles=>#<ActiveRecord::Associations::HasManyAssociation:0x00007fa3083dc978...
#      ...落落長...省略...@association_ids=nil, @association_scope=nil>} 

@association_cache回傳一個key是:styles 的hash

再加一個Type Model

class Type < ApplicationRecord
end

class Product < ApplicationRecord
  has_many :styles, class_name: 'ProductStyle'
  has_many :types
end

一次關聯兩個資料表

product = Product.includes([:styles, :types]).last
product.instance_eval{@association_cache}.keys
# => [:styles, :types] 

@association_cache會用關聯的名字作為一個個hash_key,把預加載的資料放在對應的hash value。

結語:

透過includes方法把資料cache@association_cache裡,就可以減少query資料庫的次數,是非常好用的技巧。


上一篇
[Day 16] 關聯資料表加載,解決 N+1 Query
下一篇
[Day 18] 使用 ApplicationMailer 寄Email
系列文
關於 Ruby on Rails,我想說的是23
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言