Why most backend performance issues eventually lead back to the database
In Part 1, we saw how systems collapse under pressure.
In Part 2, we saw how caching can help or hurt.
Now we look at the most common bottleneck in backend systems:
The database.
Almost every request touches it.
So when it slows down, everything slows down.
Every request depends on the database
Most backend operations rely on the database.
- fetching data
- storing updates
- validating state
This makes it a central dependency.
If the database is slow, your entire system feels slow.
There is no easy fallback.
Connection pool exhaustion
Databases support limited connections.
Under high traffic:
- all connections get used
- new requests wait in queue
- latency increases
This happens even before the query runs.
If the wait time grows, requests start failing.
Slow queries under load
Queries that look fast at low traffic become slow at scale.
Because now:
- many queries run together
- resources are shared
- contention increases
Even a small delay per query becomes a big problem when multiplied across thousands of requests.
Lack of proper indexing
Without indexes, the database scans large data to find results.
At small scale, it may work.
At large scale, it becomes expensive.
This increases:
- response time
- CPU usage
- overall system load
Indexes are one of the simplest and most ignored optimizations.
N plus 1 query problem
Instead of one efficient query, the system makes many small queries.
Example:
- fetch list
- then fetch details one by one
This increases:
- number of DB calls
- total latency
- load on database
At scale, this becomes a major bottleneck.
Write heavy operations
Writes are more expensive than reads.
Frequent writes can:
- lock rows
- block reads
- increase contention
When reads and writes happen together, they slow each other down.
No read write separation
Using a single database for everything creates pressure.
Reads and writes compete for the same resources.
A better approach:
- primary database for writes
- replicas for reads
Without this, scaling becomes harder.
Inefficient data modeling
Poor schema design creates long-term problems.
- too many joins
- deeply nested relations
- unnecessary complexity
This makes queries slower and harder to optimize.
Good design reduces work before optimization is even needed.
Unbounded queries
Queries without limits can become dangerous.
- fetching too much data
- no pagination
- large scans
These queries consume more memory and take longer to execute.
Under load, they affect other queries as well.
Locking and contention
When multiple operations try to access the same data, locks are created.
Too many locks lead to:
- waiting queries
- slower execution
- reduced throughput
This is common in write-heavy systems.
Database scaling limits
Databases have limits.
Vertical scaling can only go so far:
- CPU limits
- memory limits
- cost increases
Beyond a point, adding more power does not help.
You need better design, not just bigger machines.
Conclusion
It has limited resources and handles critical operations.
As load increases, small inefficiencies become visible.
Most performance issues are not sudden.
They build slowly and show up when the system is under pressure.
Understanding these patterns helps in avoiding common mistakes.
In the next part, we will look at rate limiting and how controlling traffic can prevent overload.
Thanks for reading.
Top comments (0)