Rails 利用 has_one + includes + select 砍掉 n+1 以及增進查詢效率

xdite · 2013年06月21日 · 最后由 xdite 回复于 2013年06月21日 · 3526 次阅读

原先 controller 裡面的 query 是這樣

@auctions = Auction.recent

view ( JSON api ) auction 裡面有一個 attribute 是 has_actual_price?

json.array! @auctions do |auction|
  json.id auction.id
  json.has_actual_price? auction.has_actual_price?
end

auction.rb 裡面 has_actual_price? 是這樣設計的

def has_actual_price?
  auction_items.where("actual_price > 0").first.present?
end

這個資料庫裡面 auction 與 auction_items 各是幾百萬資料..。所以這樣設計會有 N+1 與 slow query 的疑慮。

我的解法是在

has_one :auction_item_with_actual_price,  :conditions => "auction_items.actual_price > 0", :class_name => "AuctionItem"

把 controller 改成

@auctions = Auction.includes(:auction_item_with_actual_price).recent

這樣就榨出了接近 10 秒的 query time,要再快可以再對 has_one 限制 select 欄位,還會更快....

eager loading?

不写 has_one 就不能 eager loading 吧。 这个办法不错。不过必须限制条件是固定的,没法动态修改查询条件吧?

沒辦法 所以只是拿來 tune 特定的 query 的

需要 登录 后方可回复, 如果你还没有账号请 注册新账号