โ ๏ธ
N+1 Query Problem
Performance enemy โ solving with includes
The N+1 problem is the most common performance issue in Rails.
The problem:
# Controller
@posts = Post.all # Query 1: SELECT * FROM posts
# View
<% @posts.each do |post| %>
<%= post.user.name %> # Query N: SELECT * FROM users WHERE id = ? (every time!)
<% end %>
100 posts = 101 queries!
Solution: includes
@posts = Post.includes(:user) # Solved with 2 queries
# SELECT * FROM posts
# SELECT * FROM users WHERE id IN (1, 2, 3, ...)
3 approaches:
includesโ Rails auto-selects optimal strategypreloadโ loads via separate queries (default)eager_loadโ uses LEFT OUTER JOIN
Detection tool: The bullet gem auto-detects N+1 and shows warnings.
Architecture Diagram
N+1 Problem (101 queries!)
1. SELECT * FROM posts
2. SELECT * FROM users WHERE id = 1
3. SELECT * FROM users WHERE id = 2
4. SELECT * FROM users WHERE id = 3
... (repeated N times!)
101. SELECT * FROM users WHERE id = 100
Post.all โ post.user (query each time)
With includes (2 queries!)
1. SELECT * FROM posts
2. SELECT * FROM users WHERE id IN (1, 2, 3, ..., 100)
Done! Completed with just 2 queries
Post.includes(:user) โ preloaded
Code comparison:
Before
@posts = Post.all
After
@posts = Post.includes(:user)
Key point: <strong>One line of includes</strong> reduces 101 queries to 2 โ auto-detectable with bullet gem
Key Points
1
Recognize: accessing associations in loops causes N+1
2
Check rails log for repeated SELECT queries
3
Add includes(:association) to controller queries
4
Nested: includes(posts: :comments) or includes(:posts, :profile)
5
Auto-detect N+1 with bullet gem
6
Set strict_loading mode to raise exceptions on N+1 (Rails 6.1+)
Pros
- ✓ One line of includes improves performance by orders of magnitude
- ✓ Auto-detectable with bullet gem
- ✓ Preventable at runtime with strict_loading
- ✓ Easily verifiable via SQL logs
Cons
- ✗ Adding includes everywhere loads unnecessary data
- ✗ eager_load may duplicate data due to JOINs
- ✗ Complex relationships need optimal strategy decisions
- ✗ May increase memory usage
Use Cases
Post list + author display
Order list + product info
Categories + sub-items
Dashboard statistics page