Skip to content
BytePatterns

RDS Multi-AZ vs Read Replica: Failover or Read Scaling

8 min readBytePatterns

RDS Multi-AZ vs read replica: a synchronous standby that takes over on failure but serves no reads, or an async readable copy that lags and is promoted by hand.

RDS Multi-AZ vs read replica in one line: Multi-AZ keeps a synchronous standby in another Availability Zone that takes over automatically when the primary fails and serves no reads, while a read replica is an asynchronous copy with its own endpoint that you query, that lags behind the primary, and that you promote by hand. One is for availability, the other for read scaling, and a production database often has both. Facts come from the AWS documentation listed at the end, as of October 2026.

OptionReplicationWhat it is for
Multi-AZ DB instancesynchronous, to one standby in another zoneautomatic failover; the standby is not readable
Multi-AZ DB clustersemi-synchronous, to two readable standbysfaster failover plus a reader endpoint
Read replicaasynchronous, engine-native, up to 15 per sourceread scaling, reporting, cross-Region copies, DR

The problem it solves

Two incidents, two fixes. In the first, the zone holding the primary loses power, and every request fails until someone restores a backup elsewhere. In the second, nothing fails, but a reporting dashboard runs heavy queries against the primary and the checkout slows down. Multi-AZ answers the first and does nothing for the second; a read replica answers the second and is a weaker, manual answer to the first. Choosing one because it sounds like "a second copy" gets the wrong incident covered.

The general theory of primaries, replicas and lag is in database replication explained. This article is how RDS packages it.

The intuition

A Multi-AZ DB instance deployment is a standby, not a replica. RDS provisions a primary and a standby in different Availability Zones and replicates synchronously: a commit on the primary is not acknowledged until the standby has the data. The standby accepts no connections; it exists to take over. When the primary fails, or its zone does, or during some maintenance, RDS fails over automatically and repoints the instance's DNS endpoint at the standby, which becomes the new primary. The documentation puts a typical failover at 60 to 120 seconds, during which the application sees connection errors and must reconnect, to the same hostname. Because replication is synchronous, no committed write is lost.

A Multi-AZ DB cluster is the readable version. Available for MySQL and PostgreSQL, it has a writer and two reader DB instances in three zones. Replication is semi-synchronous: a commit waits for at least one reader to acknowledge. The readers serve reads through a reader endpoint, and failover is typically under 35 seconds. It is the choice when you want both the failover and some read capacity in one deployment.

A read replica is a copy you query. RDS creates it from a snapshot of the source and keeps it updated with the engine's own asynchronous replication: binary log replication for MySQL and MariaDB, streaming replication for PostgreSQL. It has its own endpoint, so you point reporting, analytics and read-heavy paths at it. Because the replication is asynchronous, the replica is behind the primary by some amount, exposed as the ReplicaLag metric, and a read right after a write can return the old value. MySQL, MariaDB and PostgreSQL sources allow up to 15 replicas; a replica can live in another Region, and can itself be Multi-AZ. Promoting a replica to a standalone instance is a manual operation that breaks the replication link, which makes replicas a disaster-recovery tool with a non-zero data-loss window, not an automatic failover.

So the decision is two independent questions:

  1. If the primary's zone dies, should the database come back on its own with no lost commits? Multi-AZ.
  2. Are reads overloading the primary, or needed in another Region? Read replicas.

Answering yes to both gives a Multi-AZ primary with read replicas hanging off it. Aurora reshapes the whole picture, because its replicas read a shared storage volume and double as failover targets; that is covered in DynamoDB vs RDS vs Aurora.

Watch it run

The lesson animation is the DynamoDB half of the RDS vs DynamoDB lesson, and it shows the other way AWS scales a database: by partitions. Keys hash to partitions, one hot key throttles while the others idle, and write sharding spreads it. Hold that next to RDS. Nothing in a Multi-AZ deployment or a read replica splits the data; every copy holds all of it. RDS scales writes by a bigger instance, protects against failure with a standby, and scales reads by adding whole copies. The contrast is the point: partitions divide the data, replicas duplicate it.

RDS vs DynamoDB

Step 1 of 10

Every DynamoDB item has a partition key. Three customers are about to write orders.

The same interactive animation as the lesson — step through it with the controls.

The code

A toy model of one RDS primary with a synchronous standby and an asynchronous read replica, not of any replication protocol. A write is appended to the primary and the standby together, because the commit waits for the standby; the replica catches up on its own schedule and may trail by a couple of writes:

class Node:
    def __init__(self, name, lag=0):
        self.name, self.lag, self.log, self.alive = name, lag, [], True
    def balance(self, account):
        return sum(amount for acct, amount in self.log if acct == account)

class ToyRds:
    """Toy model of one RDS primary with a synchronous standby and an async read replica."""
    def __init__(self):
        self.primary = Node("primary, us-east-1a")
        self.standby = Node("standby, us-east-1b")              # Multi-AZ: sync, not readable
        self.replica = Node("read replica, us-east-1c", lag=2)  # async: may trail by 2 writes
        self.endpoint = self.primary                            # the DNS name points here

    def write(self, account, amount):
        entry = (account, amount)
        self.primary.log.append(entry)
        self.standby.log.append(entry)          # commit waits for the standby: both have it
        behind = len(self.primary.log) - len(self.replica.log)
        if behind > self.replica.lag:           # the replica catches up on its own schedule
            self.replica.log = list(self.primary.log[:len(self.primary.log) - self.replica.lag])

    def read(self, account, from_replica=False):
        node = self.replica if from_replica else self.endpoint
        return node.name, node.balance(account)

    def fail_primary(self):
        self.primary.alive = False
        self.endpoint = self.standby             # failover: the endpoint now resolves to the standby
        self.standby.name = "new primary (was standby), us-east-1b"

Five writes to one account, then a look at each copy, then the primary dies:

db = ToyRds()
for amount in (100, 40, -25, 60, 10):
    db.write("acct-7", amount)

print("primary :", db.primary.balance("acct-7"), "from", len(db.primary.log), "writes")
print("standby :", db.standby.balance("acct-7"), "from", len(db.standby.log), "writes")
print("replica :", db.replica.balance("acct-7"), "from", len(db.replica.log), "writes  <- lag")
print("read via endpoint:", db.read("acct-7"))
print("read via replica: ", db.read("acct-7", from_replica=True))

db.fail_primary()
print("after failover:  ", db.read("acct-7"))            # same endpoint name, every committed write
print("replica still:   ", db.read("acct-7", from_replica=True))
# primary : 185 from 5 writes
# standby : 185 from 5 writes
# replica : 115 from 3 writes  <- lag
# read via endpoint: ('primary, us-east-1a', 185)
# read via replica:  ('read replica, us-east-1c', 115)
# after failover:   ('new primary (was standby), us-east-1b', 185)
# replica still:    ('read replica, us-east-1c', 115)

The standby has all five writes, because the commit waited for it, so after failover the endpoint returns the same 185 from a different zone. The replica has three, so a dashboard reading from it shows 115 until it catches up, and if the replica had been promoted instead of the standby taking over, the last two writes would be gone. That is the whole difference between synchronous and asynchronous in two numbers.

The complexity

  • Multi-AZ roughly doubles the instance and storage cost, and adds a little write latency, since each commit crosses a zone before it is acknowledged. The payoff is a failover measured in a minute or two with no lost commits.
  • A read replica costs a full instance per replica and adds no write latency, because the primary does not wait for it. The payoff is read capacity, with staleness of ReplicaLag and extra load on the primary to ship the changes.
  • A Multi-AZ DB cluster sits between the two: readable standbys and faster failover, at the price of three instances.

Where it goes wrong

  • Reading from the standby. A Multi-AZ DB instance's standby takes no connections. For reads you need a replica or a Multi-AZ DB cluster.
  • Treating a replica as high availability. Promotion is manual, and asynchronous replication means the replica may lack the last writes. Use Multi-AZ for failover.
  • Read-your-own-writes on a replica. A user saves a profile and the next page loads from the replica, which has not caught up. Route reads that follow a write to the primary, or wait on lag.
  • Not handling the reconnect. Failover changes which host the endpoint resolves to; connections to the old primary break, and clients with cached DNS may keep failing until the cached entry expires.
  • Ignoring ReplicaLag. Long-running writes or an undersized replica can push lag into minutes; alarm on it.

When it shows up in interviews

As a reliability follow-up: "the database is a single point of failure, what do you do?" The strong answer separates the two concerns, Multi-AZ for failover and replicas for reads, states that the standby serves no reads, and names replication lag as the cost of scaling reads. Certification questions test exactly the trap in the first bullet above: the standby is not a read replica.

How to say it in an interview

"Multi-AZ is for availability: RDS keeps a synchronous standby in another zone, fails over automatically by repointing the endpoint, and no committed write is lost, but the standby serves no reads. A read replica is for read scaling: an asynchronous copy with its own endpoint that I send reports and read-heavy paths to, accepting replication lag; promoting it is manual, so it is disaster recovery, not failover. In production I run a Multi-AZ primary and add replicas as read load grows, and I route reads that follow a write back to the primary."

Frequently asked questions

What is the difference between Multi-AZ and a read replica in RDS?

Multi-AZ replicates synchronously to a standby in another Availability Zone that cannot be read and takes over automatically on failure. A read replica replicates asynchronously to a readable instance with its own endpoint, used for read scaling and reporting; it lags behind the primary and is promoted manually.

Can I read from the RDS Multi-AZ standby?

Not in a Multi-AZ DB instance deployment: the standby accepts no connections and exists only for failover. A Multi-AZ DB cluster, available for MySQL and PostgreSQL, is different: its two standbys are readable through a reader endpoint. For read capacity on a DB instance, add read replicas.

How long does RDS Multi-AZ failover take?

For a Multi-AZ DB instance the documentation gives a typical failover of 60 to 120 seconds; for a Multi-AZ DB cluster, typically under 35 seconds. The endpoint hostname stays the same and points at the new primary, so the application reconnects rather than reconfigures.

Sources