在許多情況下,會需要統計一對多關聯的資料數量。舉例來說像是 User has_many Post。這時如果要統計該 user 擁有的 post 數量時,若直接使用 User.posts.size 則每次讀取頁面都會重新再統計一次,影響網站效能,而 ActiveRecord’s counter_cache 可以來幫助解決這個問題。
以 User has_many post 的例子來看:
1 2 3 4 5 6
# app/controllers/users_controller.rb classUsersController < ApplicationController defindex @users = User.all end end
# app/models/user.rb classUser < ApplicationRecord has_many :posts end
# app/models/post.rb classPost < ApplicationRecord belongs_to :user end
從 server console 中可以看到 N+1 問題,會重複的一直去撈資料庫。
1 2 3 4 5 6
SELECT "users".* FROM "users" SELECT COUNT(*) FROM "posts" WHERE "posts"."user_id" = $1 [["user_id", 1]] SELECT COUNT(*) FROM "posts" WHERE "posts"."user_id" = $1 [["user_id", 2]] SELECT COUNT(*) FROM "posts" WHERE "posts"."user_id" = $1 [["user_id", 3]] SELECT COUNT(*) FROM "posts" WHERE "posts"."user_id" = $1 [["user_id", 4]] SELECT COUNT(*) FROM "posts" WHERE "posts"."user_id" = $1 [["user_id", 5]]
# lib/migrate/reset_user_posts_count.rb namespace :migratedo desc 'Reset user posts_count counter cache' task reset_user_posts_count::environmentdo User.pluck(:id).each do|id| User.reset_counters(id, :posts) end end end