N+1 xảy ra khi load 1 collection rồi lặp để lấy association của mỗi item → 1 query lấy list + N query con.
ruby
# N+1: 1 query lấy posts + N query lấy user của mỗi post
Post.all.each { |p| puts p.user.name }
# FIX: includes eager-loads user cùng lúc
Post.includes(:user).each { |p| puts p.user.name }
# SQL: SELECT * FROM posts + SELECT * FROM users WHERE id IN (1, 2, ...)includes vs eager_load vs preload:
- includes → Rails tự chọn strategy (preload hoặc eager_load).
- preload → luôn 2 query riêng.
- eager_load → luôn 1 query JOIN (dùng khi cần filter theo association).
Gem bullet tự phát hiện N+1 trong development.