Scaling Relational Databases: Read Replicas & Connection Pooling
As web traffic scales, relational databases (PostgreSQL, MySQL, Amazon Aurora) inevitably become the primary system bottleneck. Because write operations require ACID transaction guarantees and locking on a single primary writer node, standard architecture splits read and write queries, directing read-heavy SELECT traffic across a pool of horizontally scalable Read Replicas.
Applying Little's Law to Database Concurrency
The number of active concurrent database queries on a server at any moment is governed by Little's Law: L = λ × W, where λ is Queries Per Second (QPS) and W is the average query execution latency in seconds. A database handling 5,000 QPS with 10ms average latency requires 5,000 × 0.010 = 50 active concurrent worker processes.
When Do You Need PgBouncer?
- PostgreSQL Process Model: PostgreSQL spawns a dedicated operating system process for every connected client. 500 idle connections consume gigabytes of server RAM and cause severe CPU context-switching overhead.
- Transaction Pooling Mode: PgBouncer multiplexes thousands of incoming application connections onto a small, highly optimized pool of 20 to 50 active server connections, assigning server connections only for the duration of a transaction.
- Replication Lag Monitoring: Asynchronous streaming replication can result in replica lag during heavy write spikes. Critical read-after-write workflows (e.g. rendering user profile immediately after update) should be routed to the Primary writer to prevent reading stale state.