Detecting N+1 Queries in Production Logs
Visual guide to detecting ORM issues and fixing with eager loading
Your ORM Is Lying to You
Visualizing the N+1 performance killer.
The Innocent Code
- The Loop —
users = User.all(); foreach user: print user.address.city - The Expectation — One database query to get everything.
- The Reality — One query for Users. Plus ONE query for each user to get the address.
The N+1 Mathematics
- 1 Query —
SELECT * FROM users(Returns 100 users). - N Queries — 100 x
SELECT * FROM addresses WHERE user_id = ?. - Total — 101 queries for one page load. This kills the database.
The Fix: Eager Loading
- The Instruction — Tell the ORM upfront what you need:
.include('address'). - The Result — The ORM does a
JOINor aWHERE INclause. - Efficiency — Reduces 101 queries down to 2 queries.
Detection in Logs
- Visual Pattern — Look for a wall of identical
SELECTstatements in your terminal. - Tools — Laravel Telescope, Django Debug Toolbar, or Rails Bullet.
When Lazy Loading is OK
- Single Record — Fetching details for just one user (Profile Page).
- Conditional — When the data is rarely accessed and heavy to fetch.
Summary: Fetch Smart
Do the weekly grocery shop (one big trip).
Don't treat your database like a convenience store (many small trips).
Check your query logs today.