← Back to list

Why fixing N+1 is not enough to optimize

I was looking at my GraphQL resolver, wondering, “Everything is optimized. Why is it still slow?” I found that resolving the N+1 didn’t…

Sai Teja · 2026-02-22 03:11 · 0 claps · 3.1 min read
#sqlc #graphql #design-pattern-in-golang #golang
Open on Medium ↗

Why fixing N+1 is not enough to optimize

I was looking at my GraphQL resolver, wondering, “Everything is optimized. Why is it still slow?” I found that resolving the N+1 didn’t make my GraphQL API fast. It exposed the real problems.

Background:

Though the biggest issue is resolved, that N+1 problem is fixed, it does not mean the problem is solved. Dwelling deep, there could be a bottleneck moved to somewhere else, which is what causes the GraphQL API to be slow. Let me explain how I found and solved the issue after solving N+1.

Most developers overlook the bottlenecks raised after resolving the N+1 issue, thinking that the problem is solved; however, the API still feels slow even after refactoring resolvers and batching queries.

Here is what happened with my GoLang+GraphQL project, though I used SQLC, eliminating the N+1 issue. Then I realized that the N+1 issue is the basic performance issue, not the concluding one. Subsequently, other bottlenecks unfold.

They are

  • Over‑fetching,
  • Heavy batched queries,
  • Poor SQL design, and
  • Index mismatches

Solving the Over-fetching issue

Yes, GraphQL provides flexibility, but the price has to be paid. Even after fixing the N+1 issue, I observed a pattern where data is overly fetched even when not required; however, GraphQL fetched it. This slows down the system due to the following reasons :

  • More columns fetched means wider rows,
  • More joins means heavier query plans, and
  • More serialization means slower response times

To overcome these problems, I introduce

  • lighter query variants
  • Avoided eager joins (unless explicitly required)
  • Designed GraphQL fields to map to purpose-built SQL queries, not generic ones

Here, this made me realize that eliminating N+1 does not guarantee the queries are minimal.

Dealing with Batching

The primary goal was to solve the N+1 issue, which was done successfully using batching. But batching caused another issue

A batched query looks like:

SELECT *
FROM students
WHERE house_id IN ($1, $2, $3, … $500);

This works, but at a higher cost until it gets complex, data load increases, and then it starts to fail the purpose

The large “IN” lists cause query planner confusion, and Postgres becomes inefficient using indexes as the query latency increases with the growth in data volume.

It was then that I had to rewrite the queries using JOINs, use temporary result sets, and reduce the batch sizes as much as possible where they were necessary.

Poor Query design and its impact

Since I implemented SQLC, I was under the assumption that the SQLC-generated code is optimal. I was wrong. Of course, SQLC generates type-safe and correct queries, which does not make it ideal or optimal. It trusts and compiles whatever SQL we pass.

That is where query design comes into play; SQLC is all about correctness. Performance, on the other hand, depends on the query design,n which depends on how we design.

Index mismatches

Addressed the above issues. Still, performance is not as fast as expected. Something is still missing. Indexes. They are overseen. They exist, but were not correctly used and ordered properly. Filters should match the indexes. Without verifying these, we cannot expect the performance improvements while addressing other issues. Composite indexes should be reordered with respect to query patterns. Indexes should reflect how we query, but not how we model the data.

Conclusion:

In conclusion, GraphQL performance is never a single problem; it is a full-stack problem, from design to execution. If the GraphQL API is still slow after resolving the N+1, it means we have reached the other level of optimization.


메타데이터
post_id
0bc2f86740ce
slug
why-fixing-n-1-is-not-enough-to-optimize-0bc2f86740ce
url
https://medium.com/@bsaiteja/why-fixing-n-1-is-not-enough-to-optimize-0bc2f86740ce
canonical_url
https://medium.com/@bsaiteja/why-fixing-n-1-is-not-enough-to-optimize-0bc2f86740ce
author_url
https://medium.com/@bsaiteja
status
ok
fetched_at
2026-07-16 17:31:52