Lesson 0005 · Persistence & durability
How an in-memory, single-threaded server saves to disk without freezing every client.
Redis lives in RAM — so a crash or restart would vaporize everything unless it wrote
to disk. But recall the keystone from Lesson
0001: one thread runs every command. If that thread stopped to write gigabytes
to disk, every client would stall for the whole write. This lesson is how Redis escapes that
trap — with two persistence modes (RDB snapshots and the AOF
log) and one beautiful OS trick (fork() + copy-on-write) that lets the single
thread keep serving while a child process does the disk I/O.
RDB writes a compact binary snapshot of the whole dataset at intervals —
"a very compact single-file point-in-time representation of your Redis data." You
configure triggers like save 60 1000 (snapshot if ≥1000 keys changed in 60s), or
fire one manually.
— Redis Docs — Persistence
BGSAVE — snapshot in the background via a forked child; the
main thread keeps serving. — Docs — BGSAVESAVE — snapshot in the main process, blocking every client
until done. Almost never used in production. — Docs — SAVEAOF takes the opposite approach: "logs every write operation received by the server. These operations can then be replayed again at server startup, reconstructing the original dataset." It's a write-ahead-style journal of commands, not a snapshot of state. — Redis Docs — Persistence
How often the log is flushed to disk (fsync) is the durability dial —
appendfsync:
| appendfsync | fsync cadence | Worst-case loss |
|---|---|---|
always | every write | ≈ nothing — "very very slow, very safe" |
everysec (default) | once per second | ~1 second of writes |
no | OS decides (~30s on Linux) | up to ~30 seconds |
"The suggested (and default) policy is to fsync every second. It is both fast and relatively safe." (antirez notes the worst case is bounded by ~2 seconds, not exactly 1.) — Docs · antirez — persistence demystified
Here's how a single thread persists without blocking. To snapshot (or rewrite the AOF),
Redis calls fork(): the OS creates a child process that does all
the disk I/O, while the parent keeps executing commands. "The parent process will never
perform disk I/O."
— Redis Docs
The child doesn't copy the dataset. After fork(), parent and child
share the same physical memory pages, read-only. Only when the parent
modifies a page does the OS copy that page — "Redis [benefits] from
copy-on-write semantics." So the extra memory a snapshot needs is proportional to how
much data changes during the save, not the size of the whole dataset. The
child writes a frozen, consistent point-in-time view.
fork() itself isn't free: "fork() can be time consuming if the dataset is
big, and may result in Redis stopping serving clients for some milliseconds or even for one
second if the dataset is very big." On the single thread, that fork pause is
head-of-line blocking. Big instances + heavy writes (more pages to copy) = bigger spikes.
— Redis Docs
The AOF has the same growth-and-fork story: the log keeps growing, so
AOF rewrite (BGREWRITEAOF) forks a child that writes "the
shortest sequence of commands needed to rebuild the current dataset" — compacting the log
using the same copy-on-write trick.
— Redis Docs
| RDB (snapshot) | AOF (log) | |
|---|---|---|
| Durability | Lose minutes since last snapshot | Lose ≤ ~1s (with everysec) |
| File | Compact single binary — great for backups/DR | Larger; needs periodic rewrite to shrink |
| Restart speed | Faster with big datasets | Slower — replays the log |
| Steady-state cost | Fork only when snapshotting | Appends continuously (+ rewrites) |
Use both. The docs: "use both persistence methods is if you want a degree of data safety comparable to what PostgreSQL can provide you." RDB gives you cheap backups and fast restarts; AOF gives you a tight data-loss window. AOF alone is discouraged because an occasional RDB is invaluable for backups and faster restarts. — Redis Docs
Answer from memory — effortful recall builds retention. Two questions revisit earlier lessons on purpose (spacing).
RDB persists the dataset by having Redis:
Copy-on-write means the snapshot's extra memory is proportional to:
With appendfsync everysec, a crash loses at most about:
For data safety comparable to PostgreSQL, the docs recommend:
Interleave — a fork() for a big snapshot can cause what, per Lesson 0001?
Interleave — which command atomically elects one request to rebuild a cache key?
You need a local Redis and redis-cli.
redis-cli CONFIG GET save # the automatic snapshot rules
redis-cli SET a 1
redis-cli BGSAVE # fork a child, snapshot in background
redis-cli INFO persistence | grep -E 'rdb_last_bgsave_status|rdb_changes_since_last_save|rdb_last_save_time'
redis-cli CONFIG SET appendonly yes
redis-cli CONFIG GET appendfsync # -> everysec (the default)
redis-cli MSET x 1 y 2 z 3
redis-cli BGREWRITEAOF # compact the log via a forked child
redis-cli INFO persistence | grep -E 'aof_enabled|aof_last_bgrewrite_status|aof_rewrite_in_progress'
After BGSAVE, what does rdb_changes_since_last_save reset to, and
why? And if you killed Redis one second after a write with only everysec AOF, how
much would you lose? Commit, then check.
Clean up: redis-cli CONFIG SET appendonly no, then redis-cli FLUSHALL.
Bring any surprising INFO persistence field to your teacher.
Redis Docs — Persistence
is the authoritative page: RDB vs AOF, the fork/COW mechanism, appendfsync, and
the "use both" guidance. For the author's own deep dive, read
antirez — Redis persistence demystified.