Merhabalar, bugün sizlere içinde binlerce ibret olan diyemeyeceğim ama birkaç ufak titreme yaşatacak bir şeyler anlatacağım.
Rails ile bir blog uygulaması yazdığımızı hayal edelim. Elimizde Post ve Comment şeklinde modellerimiz olsun. Biz de anasayfada yazıların başlıklarını ve yorum sayılarını listelemek isteyelim.
Processing by PostsController#index as HTML
Post Load (0.2ms) SELECT "posts".* FROM "posts"
(0.1ms) SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 1]]
(0.1ms) SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 2]]
(0.1ms) SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 3]]
(0.1ms) SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 4]]
(0.1ms) SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 5]]
Hooaayyydaaaa!!!?!1?
Processing by PostsController#index as HTML
Post Load (0.3ms) SELECT "posts".* FROM "posts"
Comment Load (0.2ms) SELECT "comments".* FROM "comments" WHERE "comments"."post_id" IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
Şükür 2 sorguya düşürdük.n+1
query problemini de giderdik. Bu arada n+1
query probleminin tespiti için Bullet gem’ini kullanabilirsiniz.
Counter Cache
Fakat bunun yerine başka bambaşka bir olay var. counter_cache kullanarak yazıya bağlı yorumların sayısını daha verimli bir şekilde öğrenebiliriz.
posts tablosuna comments_count sütunu ekleyecek bir migration yazalım ve ardından Comment modelimizde düzenleme yapalım.
Counter cache ekledikten sonra size veya comments_count ile tek sorgu yaparak yorum sayısını öğrenebiliriz. Eğer count kullanırsak comments tablosuna sorgu atar.
> Post.first.comments.count
SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT 1
SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 1]]
4
> Post.first.comments_count
SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT 1
4
> Post.first.comments.size
SELECT "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT 1
4