Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
DatabasesJune 20, 20264 min read

Killing N+1 queries at the database layer: A practical guide

Killing N+1 queries at the database layer is the fastest way to stabilize your app. Learn how to identify, debug, and eliminate performance-killing bottlenecks.

databaseperformancesqleloquentoptimizationbackendPostgreSQLMySQLRedis
Scrabble tiles spelling 'DATA' on a wooden table with a blurred plant background.

I remember sitting at my desk around 2:00 AM, staring at a dashboard that was screaming red. Our primary order processing service was timing out, and the culprit wasn't a complex algorithm—it was a classic N+1 query pattern that had survived our staging environment.

We’d built a feature to list recent orders alongside customer details. In development, with ten records, it was lightning fast. In production, with a user list spanning thousands, the application was firing one initial query for the orders and then an individual SELECT for every single customer. The latency jumped from 20ms to roughly 850ms, effectively locking up our connection pool.

The Anatomy of the N+1 Problem

The N+1 problem occurs when your application performs one query to fetch a set of records, and then, for each of those records, executes an additional query to fetch related data. If you fetch 100 orders, you’re hitting the database 101 times.

Most of us learn the basics of Eloquent basics: models, relationships, and your first queries early on, but it's easy to forget that convenience often masks the underlying network round-trips. When you iterate through a collection and call a relationship property, the ORM is often lazy-loading that data in real-time.

Why We Failed at First

Our first instinct was to just "add more indexes." We looked at our Indexing Strategy for App Developers: Stop Slow Queries and assumed the database was the bottleneck. We added a composite index on the customer_id and created_at columns.

It didn't help.

The database wasn't struggling to find the rows; it was struggling to process 101 separate requests. The overhead of opening and closing connections, parsing SQL, and managing transactions for each small query was the real killer. We weren't fighting an indexing issue; we were fighting an architectural one.

Killing N+1 Queries at the Database Layer

Instead of relying on the ORM to "magically" fetch related data, we shifted our focus to eager loading and database-level aggregation.

If you are working in a framework like Laravel, you already know about with(). However, sometimes you need more control. When Eliminating N+1 queries in Eloquent: A Pragmatic Approach, I usually recommend looking at the raw SQL generated by your queries.

If you're stuck, use this approach:

  1. Use a Debugger: Tools like Laravel Debugbar or simple log-based query logging will show you exactly how many queries are hitting the DB.
  2. Eager Load: Ensure you are using with() to pull related models in a single WHERE IN (...) query.
  3. Denormalize or Aggregate: For high-traffic dashboards, sometimes it’s better to perform a JOIN or use a subquery to pull the data you need directly into the result set.

For example, instead of fetching customers separately, use a join:

SQL
SELECT orders.*, customers.name as customer_name 
FROM orders 
JOIN customers ON orders.customer_id = customers.id 
WHERE orders.status = 'pending' 
LIMIT 50;

This reduces 51 queries to exactly one. The database is optimized to handle joins; your application server is not optimized to handle the network latency of 50 extra round-trips.

When to Go Further

Sometimes, even eager loading isn't enough. If you’re performing complex transformations on the data, consider moving that logic into the database. I’ve had success with Laravel Eloquent Performance: Mastering PostgreSQL Generated Columns to pre-calculate values that would otherwise require multiple queries to compute.

By pushing this logic into the schema, you ensure that the data is ready before the application even asks for it. It turns a compute-heavy read operation into a simple index scan.

Caveats and Lessons Learned

I still see developers trying to solve N+1 issues by simply caching the result of every individual query in Redis. While caching helps, it’s a band-aid. If your cache expires, your database will still get crushed by that burst of 100+ queries.

Next time I tackle this, I’ll prioritize profiling the query count during the PR review process itself. It’s much cheaper to catch a nested loop in a controller than it is to debug it at 2:00 AM on a Tuesday. We're still refining our automated test suite to fail builds if the query count exceeds a certain threshold—a "query budget," if you will. It’s a work in progress, but it beats the alternative.

Back to Blog

Similar Posts

Scrabble tiles spelling 'DATA' on a wooden table with a blurred plant background.
DatabasesJune 20, 20264 min read

When to denormalize your database for production performance

Denormalize your database only when read latency becomes a bottleneck. Learn to evaluate the trade-offs between schema complexity and query speed.

Read more
Close-up of a smartphone showing Python code on the display, showcasing coding and technology.
Databases
June 20, 2026
4 min read

Indexing Strategy for App Developers: Stop Slow Queries

Master an indexing strategy for app developers to fix slow production queries. Learn how to read EXPLAIN plans, pick the right columns, and avoid overhead.

Read more
Multiple stacks of blue medical face masks arranged on a white background, emphasizing cleanliness and healthcare.
DatabasesJune 20, 20264 min read

Redis Caching Patterns That Prevent Stampedes in Production

Redis caching patterns that prevent stampedes are essential for scaling. Learn how to stop the thundering herd effect and keep your backend performance stable.

Read more