Redis Persistence: RDB Snapshots vs AOF Logs, and Whether Data Is Actually Lost
· tech
📑 Contents
- Two approaches: snapshots vs a running log
- fsync: picking a spot between “safe” and “fast”
- Behind the scenes: fork + copy-on-write (and the memory-spike trap)
- Don’t misread it: Redis persistence isn’t a financial-grade guarantee
- redis-cli: the three knobs for persistence
- Reflections
- ”Will it lose data” isn’t yes/no; it’s “how much performance will you trade for how much safety”
- What fork + copy-on-write taught me: the most elegant concurrency is often “share, and copy only on write”
- Get Redis’s role in the architecture straight, and half the persistence anxiety disappears
“Redis is an in-memory database; lose power and all the data’s gone” — that line is half right, half wrong. Right in that it lives mainly in memory; wrong in that it does have persistence, can write data to disk and restore it after a restart. Its persistence semantics are just weaker than a traditional database’s, and you have to understand them to use them right. Redis gives you two mechanisms with completely different approaches: take snapshots (RDB) and keep a running log (AOF).
Two approaches: snapshots vs a running log
RDB (Redis Database) is a snapshot: at some point in time, dump the whole memory into one compact binary file (dump.rdb). AOF (Append Only File) is an operation log: append every write command to a file, and on restart replay those commands to rebuild the state. They aren’t either/or — you can enable both and take the best of each with hybrid mode.
fsync: picking a spot between “safe” and “fast”
AOF has a key setting: after writing to the log, how often is it actually fsynced to disk? That choice directly decides “how much you can lose at most”. Lay every option on one spectrum and you’ll find persistence has no “right answer”, only “where you stand between safety and performance”:
fsync is one of three: always (flush on every write command, lose 0 but slowest), everysec (flush once a second, the default, lose at most 1 second — the sweet spot for the vast majority of cases), no (leave it to the OS, fastest but least safe). Add RDB and hybrid mode, and you see persistence isn't an "on or off" switch but a spectrum. Where you stand depends on whether this data can lose a second, a minute, or not a single entryBehind the scenes: fork + copy-on-write (and the memory-spike trap)
A question you may have wondered: Redis is single-threaded, so how does it save a snapshot (BGSAVE) or rewrite the AOF without blocking the main process from serving? The answer is fork + copy-on-write (COW): Redis forks a child process; the child “freezes” a snapshot of memory as of that moment and writes the file at leisure; the main process keeps serving. Parent and child initially share the same memory pages, and only when the main process modifies a page does the OS copy that page for it (copy only on write) — untouched pages take no extra space at all.
But a trap hides here: if writes are very frequent during the snapshot, more and more pages get modified and more and more COW copies are made, and memory can spike briefly, in the worst case close to double. So when running Redis in Production, always leave enough memory headroom, or a BGSAVE triggers, memory climbs and you OOM — this is the culprit behind many a “fine all day, dies whenever it backs up”.
Don’t misread it: Redis persistence isn’t a financial-grade guarantee
One honest conclusion to finish: even with AOF always, Redis isn’t giving you a “never lose a single entry” strong-durability guarantee — by design it leads with speed. The real value of its persistence is “warm up fast after a restart, avoid a stampede of origin fetches hammering the database behind it”, not serving as the source of truth you can’t afford to lose. That echoes the first post‘s positioning exactly: Redis is the hot data layer; the authoritative copy should live in the database behind it; what’s in Redis is a rebuildable acceleration layer. Set that role straight, and the anxiety of “if Redis dies, is the data gone” downgrades from “disaster” to “a slow trip to the origin for a while”.
redis-cli: the three knobs for persistence
The trade-offs above land as three knobs (all settable with CONFIG SET, no restart):
# view / adjust settings
CONFIG GET save # RDB snapshot triggers, e.g. "3600 1 300 100 60 10000"
CONFIG SET appendonly yes # enable the AOF log
CONFIG GET appendfsync # AOF flush policy: always / everysec (default) / no
CONFIG REWRITE # write the current settings back to redis.conf (or a restart reverts them)
# trigger manually and inspect
BGSAVE # fork in the background and save an RDB
BGREWRITEAOF # compact the AOF file
LASTSAVE # timestamp of the last successful save (use it to confirm nothing's stuck)
INFO persistence # rdb_last_bgsave_status, aof_enabled, aof_last_write_status…
save (how often RDB snapshots), appendonly (whether to keep the log), appendfsync (how often to flush) — those three knobs decide where you stand on the “durability ↔ performance” spectrum. Remember CONFIG SET only changes the running instance; you need CONFIG REWRITE to save it to the config file, or a restart wipes your changes.
Reflections
”Will it lose data” isn’t yes/no; it’s “how much performance will you trade for how much safety”
When I first learned Redis persistence, I kept looking for the one “most correct” setting, until I realised the question was wrong. Persistence has no standard answer, only coordinates on a trade-off: for this data, can you accept losing a second? A minute? Not a single entry? — different answers, different mechanisms. For a session cache, everysec is more than enough; for anything involving money, it shouldn’t live only in Redis in the first place. Now, whenever I configure persistence (not just Redis), I first ask “how much loss can we tolerate” and then go back and choose the setting, rather than blindly chasing “safest” — because safest is usually also slowest, and you may not need it at all. Define the failure you can accept first, then pick the technology — that order matters more than anything.
What fork + copy-on-write taught me: the most elegant concurrency is often “share, and copy only on write”
fork + COW is a design I love. The problem it solves is “how to take a consistent snapshot of something that keeps changing, without stopping service” — and its answer isn’t “lock it and copy it” (too expensive) but “share first, and only when someone wants to write, copy that one small piece”. This “copy on write” idea is everywhere once you look: immutable data structures in programming languages, a database’s MVCC snapshots, even Git’s object store. Their shared wisdom: most things won’t actually be modified, so don’t copy everything up front; pay only for the changes that really happen. Once you understand COW, a lot of seemingly magical “lock-free snapshots” stop being magical.
Get Redis’s role in the architecture straight, and half the persistence anxiety disappears
I’ve seen plenty of teams agonise over “how strong should Redis persistence be”, but the root of that anxiety is not having settled what role Redis plays in the architecture. If it’s an acceleration layer and the data can be rebuilt from the database behind it, then persistence only needs “restart fast, fetch less from origin”, and everysec or hybrid mode is plenty; if you find you need it to “never lose a single entry”, the real problem isn’t “how to set persistence” but “this data shouldn’t live only in Redis”. Many technical anxieties, traced to the bottom, aren’t technical problems but positioning problems — think through each component’s responsibility in the system, and half the agonising dissolves on its own. It’s also the deepest lesson of my years doing architecture: first sort out who is the truth and who is the accelerator, then talk about settings.