How to spread read traffic horizontally without losing sight of consistency
Most web applications read from the database far more often than they write to it, which turns the primary server into a bottleneck even though writes make up only a small share of the load. Read replicas distribute read traffic across additional server copies and take noticeable pressure off the primary.
Inhaltsverzeichnis
- 1. What Are Read Replicas
- 2. Why Scaling Through Replicas Makes Sense
- 3. Replication Mechanisms Overview
- 4. Replication Lag as a Practical Challenge
- 5. Strategies Against Lag-Driven Inconsistency
- 6. Read-Write Splitting in the Application Layer
- 7. Monitoring Replication Lag
- 8. Limits and Pitfalls
- 9. Summary and Practical Recommendation
- 10. Zusammenfassung
- 11. FAQ
1. What Are Read Replicas
A read replica is a continuously synchronized copy of a database that serves only read traffic, while all write traffic still lands on the so-called primary server. The replica continuously receives changes from the primary, typically via a replication stream, and applies them in the same order they occurred on the primary, so it rebuilds the same data state over time.
From the application's point of view, one logical database suddenly consists of multiple physical servers: a primary for write operations and any number of replicas for read operations. This architecture is especially effective for applications with a read-to-write ratio of ten to one or higher, which is typical for most online stores, content sites, and reporting systems.
2. Why Scaling Through Replicas Makes Sense
A single database server has a hard ceiling on CPU, memory, and I/O throughput, and vertical scaling through bigger hardware eventually runs into technical and economic limits. Horizontal scaling by adding more replicas, on the other hand, lets you spread read load almost linearly across as many additional servers as needed, as long as the application splits read requests accordingly.
The benefit shows clearly during load spikes, for example a sale event on an online store: instead of upgrading a single, increasingly expensive primary server, you can temporarily add more replicas that serve nothing but product pages and category listings, while the primary stays focused on orders and cart changes. That keeps infrastructure costs down in normal operation while still leaving headroom for peak load.
3. Replication Mechanisms Overview
In MySQL and MariaDB, replication is typically based on the binary log, in which the primary records every data-changing operation. The replica continuously reads this log and applies the changes locally, with a distinction between asynchronous, semi-synchronous, and, in some systems like Galera Cluster, synchronous replication. In PostgreSQL, the write-ahead log plays the same role and is transferred to replicas via streaming replication.
Asynchronous replication is the default case and offers the best write performance on the primary, since it doesn't have to wait for replica confirmation before reporting a write as complete. The price is a possible time gap between primary and replica, whereas semi-synchronous replication at least waits for confirmation that the log entry was received, reducing the risk of data loss on a primary failure at the cost of slightly higher write latency.
4. Replication Lag as a Practical Challenge
The most important pitfall when using read replicas is replication lag, the time gap between a write completing on the primary and it becoming visible on the replica. Under normal load this lag is often a matter of a few milliseconds, but it can grow to several seconds or even minutes under heavy write load, slow transactions, or network issues.
In practice this shows up when, for example, a user is redirected straight to a confirmation page after submitting a contact form, and that page tries to load the just-saved record from a replica that hasn't received it yet. The result would be a confusing empty page or an error message even though the write technically succeeded, which looks to the user like a bug in the application.
5. Strategies Against Lag-Driven Inconsistency
A common solution is the so-called read-your-writes pattern, where the application remembers that a user recently wrote data and, for a short window, say the next few seconds, reads exclusively from the primary instead of a replica. This marker can be stored in the session and expires automatically once enough time has passed for replication to catch up.
Alternatively, some database systems support querying a specific replication position, so a read request can explicitly wait until the replica has reached at least that state before responding. This approach is more precise than a fixed time window, but it introduces variable latency and should only be used for requests where consistency matters more than a guaranteed short response time.
6. Read-Write Splitting in the Application Layer
Read-write splitting refers to the logic that decides whether a given database request is sent to the primary or to a replica. That decision can be made either by a dedicated proxy such as ProxySQL or HAProxy, which analyzes SQL statements and routes them automatically, or directly in application code, where every connection is explicitly tagged as read or write.
The advantage of implementing this in the application layer is control over edge cases like the read-your-writes pattern described earlier, which a plain SQL parser in a proxy struggles to handle correctly. The PHP example below shows a simple connection factory that decides which connection to use based on the statement type and a session flag.
final class ReadWriteConnectionRouter
{
public function __construct(
private readonly PDO $primaryConnection,
private readonly PDO $replicaConnection,
private readonly SessionInterface $session,
) {
}
public function getConnectionFor(string $sqlStatement): PDO
{
$isWrite = (bool) preg_match(
'/^\s*(INSERT|UPDATE|DELETE|REPLACE)\b/i',
$sqlStatement
);
if ($isWrite) {
// Read from the primary for a short window after a write
$this->session->set('recent_write_until', time() + 5);
return $this->primaryConnection;
}
$recentWriteUntil = (int) $this->session->get('recent_write_until', 0);
if ($recentWriteUntil > time()) {
return $this->primaryConnection;
}
return $this->replicaConnection;
}
}
7. Monitoring Replication Lag
Without continuous monitoring, replication lag stays invisible until it shows up as a concrete user-facing problem, which is why dedicated alerting is essential. In MySQL, the command SHOW REPLICA STATUS returns the value Seconds_Behind_Source as a rough approximation of current lag, while PostgreSQL exposes comparable figures through the system view pg_stat_replication.
For production systems it's worth sending this metric to a monitoring system like Prometheus on a regular basis and defining a threshold beyond which a replica gets automatically pulled out of the load balancer. That prevents users from seeing stale data on a severely lagging replica, while the remaining, more up-to-date replicas automatically absorb the load.
8. Limits and Pitfalls
Read replicas don't scale every kind of load equally well: complex analytical queries with many joins or aggregations load a replica just as heavily as the primary and often benefit more from a dedicated reporting system or data warehouse than from more replicas. Write load itself also can't be reduced through replicas, since every replica has to replicate and apply the exact same write load as the primary.
An often underestimated aspect is operational complexity: failover scenarios, where a replica gets promoted to become the new primary, must be tested carefully, since misconfigured applications could otherwise keep writing to the old, possibly inconsistent primary. On top of that, infrastructure costs and maintenance effort for backups, patches, and version upgrades grow linearly with every additional replica.
9. Summary and Practical Recommendation
Read replicas are a proven way to scale read-heavy applications horizontally without taking on the full complexity of a fully distributed, sharded database system. The key success factor is planning for replication lag as a normal part of the architecture from the start, rather than treating it as a rare edge case that only surfaces after the first user complaint.
Anyone introducing read-write splitting should start by moving uncritical read requests, like product listings or blog articles, to replicas, and only gradually extend to other areas afterward, while consistency-critical sections like checkout and account balance deliberately stay on the primary. That produces an architecture that gains performance without sacrificing reliability.
| Replication Type | Write Latency on Primary | Data Loss Risk on Failure | Typical Use |
|---|---|---|---|
| Asynchronous | Very low | Possible (latest changes) | Default case for read scaling |
| Semi-synchronous | Slightly higher | Low | When data safety matters more than latency |
| Synchronous (e.g. Galera) | Noticeably higher | Practically none | Highly available cluster setups |
| Read-your-writes via primary | Unchanged | Not applicable | Forms with an immediate confirmation page |
Mironsoft
Web performance, Core Web Vitals, and load time optimization
Load times that don't make users bounce before the page is even visible?
We review existing websites for slow Core Web Vitals, bloated JavaScript bundles, and unnecessary render blockers, then build a performance foundation that stays measurable instead of just looking good once.
Performance Audit
Systematically measuring and fixing Core Web Vitals, load waterfall, and render blockers.
Bundle Optimization
Specifically reducing JavaScript and CSS bundle size and improving code splitting.
Monitoring Setup
Establishing continuous performance monitoring instead of a one-time snapshot.
10. Zusammenfassung
Database Read Replicas
Technique
Spread read traffic across synchronized server copies
Effect
Horizontal scaling for read-heavy applications
Challenge
Replication lag between primary and replica
Solution
Read-write splitting with a read-your-writes pattern