[Rails] 透過計數快取 Counter Cache 提升效能

Steven C.
From Zen to Code
Published in
3 min readJun 9, 2019

在許多場合需要統計 has_many 一對多關聯的資料數量,若使用單純的 Rails 關聯查詢,則每次讀取頁面都會重新再統計一次,影響網站效能。

情境

專案Project model has many 任務Task.

Project model

project.rb

projects_controller.rb

projects_controller.rb

views/projects/index.html.erb

projects_index.html.erb

讀取頁面時就會出現 N+1 Query 問題,1 個專案若有 3 個任務便會做 3 + 1 次查詢:SELECT “tasks”.* FROM “tasks” WHERE “tasks”.”project_id” = ?

N + 1 Query

使用方法

projects table 中建立 tasks_count 欄位,將計數存在其中,
更新專案的任務數量時,counter cache 會同步更新到對應的欄位裡面。

建立 migration

rails g migration add_tasks_count

編輯剛建立的 migration 檔:

在程式碼第一行加入 # frozen_string_literal: true 凍結此檔,可以稍微加快往後的讀取速度。

add_tasks_count.rb

執行 rails db:migrate

編輯 Task model

開啟 counter cache:

task.rb

如果想指定其他欄位儲存 counter cache 請改寫為

counter_cache: ‘COLUMN_NAME’

指定 vote 欄位作為紀錄用

確認資料表內容 schema.rb

schema.rb

建立 rake task 更新 counter cache

更新既有 projects 的 tasks count lib/tasks/update_tasks_count.rb

這邊使用我在五倍紅寶石學到的技巧,將這段 rake task 整理在 namespace db 之下。

update_tasks_count.rb

查看指令確認一下沒有寫錯:rails -T

$ rails -T

執行 rails db:updste_tasks

$ rails db:updste_tasks

重啟伺服器

再次讀取頁面查看 SQL queries 只剩一行: SELECT “projects”.* FROM “projects”

禮成,奏樂!

大功告成!

--

--

Steven C.
From Zen to Code

“Life’s like a movie, write your own ending.” — Jim Henson