jongkwan.dev
Development · Essay №064

Redis Persistence and Replication

Separating persistence (RDB and AOF), which revives a single node, from replication, which makes copies, down to the data loss each one still allows.

Jongkwan Lee2026년 3월 16일8 min read
Contents

Persistence restores one node from disk; replication keeps copies on other nodes. The two axes solve different problems.

Persistence and replication are different problems

Redis is an in-memory store. When the process dies, memory goes with it, so survival splits into two paths.

Persistence writes a node's data to disk so it can be restored after a restart. RDB and AOF belong here. Replication copies the same data to other nodes so that another node can take over when one dies.

The measure that separates them is the recovery point objective (RPO), the amount of data loss you are willing to accept during a failure. For persistence, RPO is set by the disk fsync interval; for replication, by replica lag.

RDB snapshots

RDB (Redis database snapshot) writes the entire memory contents at a point in time into a single file. Like a photograph, it captures only the state at that instant.

Save timing is set by save rules. save 900 1 means a background save starts if at least one key changes within 900 seconds (15 minutes).

conf
save 900 1
dbfilename dump.rdb
dir ./

Saving happens in a child process created by fork(). While the child writes the memory snapshot to dump.rdb, the parent keeps serving client requests, so the service does not stall during a snapshot.

RDB recovers quickly. A single file is easy to back up and copy, and even a large one reloads within seconds (estimated, from study notes). Its weakness is the loss window. With only save 900 1 in place, a worst case loses 15 minutes of writes outright.

There is a cost, too. On large datasets, fork() produces latency spikes ranging from a few milliseconds up to a second (estimated). After the fork, copy-on-write means a burst of writes can nearly double memory usage for a while.

text
Peak memory = original + write rate × snapshot duration
e.g. 8GB + (10MB/s × 10s) = about 8.1GB

The AOF log

AOF (append-only file) appends every write command that modifies data to a log in order. Where a snapshot stores the outcome, AOF stores the process that produced it.

When the log reaches disk is set by appendfsync. Its three values trade durability against performance.

appendfsyncfsync timingMaximum lossNotes
alwaysEvery commandNearly noneSlowest
everysecOnce per secondUp to 1 secondBalanced, the recommended value
noLeft to the operating systemUntil the buffer flushesFastest

The middle row is the usual choice. Production setups generally default to everysec.

everysec calls fsync once a second, and latency spikes at that moment while the disk I/O completes. A slow HDD lands in the 10-100ms range, a fast NVMe in the 1-5ms range (estimated, from study notes). Reducing the loss window to one second costs you one latency spike per second.

The log keeps growing and files reach hundreds of megabytes. BGREWRITEAOF rewrites the current dataset as a minimal sequence of commands to shrink the file. A child process builds the new file, and Redis 7.0+ swaps it in atomically. With aof-load-truncated yes, a tail truncated by an unclean shutdown is trimmed automatically during load.

The drawbacks are size and speed. An AOF file can grow 5 to 10 times larger than an RDB file (estimated), and recovery replays every command, which is slower than loading an RDB.

The RDB and AOF hybrid

Since Redis 4.0 the two formats can live in one file. The single line aof-use-rdb-preamble yes makes the head of the file produced by an AOF rewrite use the RDB format.

The new AOF file has two regions. The RDB preamble at the front holds the full state at rewrite time in compressed form, and the AOF commands behind it record only the changes since then. Recovery loads the RDB quickly and then replays the short AOF tail.

conf
appendonly yes
appendfsync everysec
aof-use-rdb-preamble yes
MethodMaximum loss (RPO)Recovery speedFile size
RDBSnapshot interval (e.g. 15 minutes)FastSmall
AOF (everysec)Up to 1 secondSlowLarge
HybridUp to 1 secondFastMedium

The hybrid gets RDB's recovery speed and AOF's one-second loss bound in a single file, which is why the configuration above is the recommended production default.

Master-replica replication

Replication streams writes from one master to several replicas to keep copies in sync. Reads spread across replicas, and if the master dies a replica is promoted, which is how high availability (HA) is built.

A new replica starts with a full synchronization.

The master builds a snapshot and hands it to the replica, then keeps streaming every write command issued after that point. The replica loads the snapshot and follows the command stream to stay in the same state.

The key point is that this propagation is asynchronous by default. The master returns success to the client as soon as it accepts a write, and the replica applies it afterward. If the master fails before an acknowledged write reaches a replica, that write can disappear.

A safeguard can be attached to the master as a condition.

conf
min-replicas-to-write 1
min-replicas-max-lag 10

If fewer than one replica is connected with lag under 10 seconds, the master refuses writes. Blocking writes that would have no copy reduces the loss risk.

PSYNC and the replication backlog

Doing a full synchronization every time a connection drops and reconnects is expensive. PSYNC (partial resynchronization) resends only the changes made during the disconnection, avoiding that cost.

The master keeps recent commands in a circular buffer called the replication backlog. If the replica returns soon, only the commands still in the backlog are resent. If the outage lasted long enough to fall out of the backlog, it degrades to a full synchronization.

conf
repl-backlog-size 256mb
repl-backlog-ttl 3600
TypeTriggerData transferredDuration
Full syncFirst connection, long outageFull RDB plus subsequent commandsUp to several minutes (estimated)
Partial resyncShort outageRemaining backlog commandsSeconds (estimated)

The larger the backlog and the longer the ttl, the longer an outage partial resynchronization can absorb.

A full synchronization normally writes the RDB file to disk before sending it. Diskless replication skips the file step and streams straight from memory to the socket.

conf
repl-diskless-sync yes
repl-diskless-sync-delay 5

This helps on slow disks, and the delay lets several replicas that arrive within the window synchronize together. The catch is that a network drop mid-transfer forces a restart from the beginning.

Narrowing loss with WAIT

To narrow the loss window of asynchronous replication, use the WAIT command. WAIT numreplicas timeout blocks until the write reaches the given number of replicas or the timeout expires.

python
await redis.set("important_key", "value")
acked = await redis.execute_command("WAIT", 2, 1000)
if acked < 2:
    # two replicas did not acknowledge within 1 second
    ...

WAIT 2 1000 waits up to one second for two replicas to acknowledge and returns how many actually did.

WAIT still does not guarantee strong consistency. If the master dies the instant only one replica has acknowledged, and the replica that did not acknowledge is promoted, that write is absent from the new master. WAIT lowers the probability of loss without eliminating it. For data where loss is unacceptable, Redis alone is not enough and a consensus-based store has to sit alongside it.

Operating criteria and monitoring

Replication state is visible through INFO replication.

bash
redis-cli INFO replication
text
role:master
connected_slaves:2
slave0:ip=10.0.0.2,port=6379,state=online,offset=1000000,lag=0
repl_backlog_active:1
repl_backlog_size:268435456

role distinguishes master from replica, and the lag value on each slave line shows how far behind that replica is. Consistently high lag is a signal that the replication loss window is that wide.

Pick the combination that matches your requirements. A cache where loss does not matter can turn persistence off entirely. If only restart recovery is needed, the hybrid (RDB preamble plus everysec) is enough. If node failure has to be survivable, add replicas and min-replicas protection on top of the hybrid.

Summary

Persistence and replication are two axes of the same durability problem. Persistence restores one node from disk; replication keeps copies on multiple nodes so a node failure is survivable.

RDB gives fast recovery and small files but loses everything between snapshots, while AOF with everysec narrows loss to one second at the cost of a much larger file. The hybrid pulls both advantages into one file through the RDB preamble. Replication is asynchronous by default, so even acknowledged writes can vanish, and WAIT and min-replicas narrow that window without closing it. Decide the acceptable loss (RPO) first, then choose the fsync interval and replica protections to match, and the design will hold.