Choosing RDB vs AOF

Redis keeps your entire dataset in RAM for speed, but RAM is wiped clean the moment the process stops. If you want your data to survive a restart, a crash, or a power loss, you need to write it to disk — and Redis gives you two very different mechanisms for doing that: RDB (point-in-time snapshots) and AOF (an append-only log of every write). They aren’t just two settings toggles; they represent a genuine tradeoff between performance, file size, and how much data you can afford to lose. This lesson explains exactly how each one works under the hood so you can pick the right one — or the right combination — for your workload.

Overview / How RDB and AOF Work

RDB (Redis Database file) is a compact, binary snapshot of your entire keyspace at a single moment in time. When a snapshot is triggered, Redis needs to write a consistent copy of memory to disk without stopping the main thread from serving clients. It does this by calling the operating system’s fork(), which creates a child process that shares the parent’s memory pages via copy-on-write (COW). The child process serializes those pages to a temporary file and renames it over the previous dump.rdb once finished. The parent (main) thread keeps handling commands the entire time — it only pays a brief cost for the fork itself. This is why BGSAVE (background save) is non-blocking, while the plain SAVE command runs the entire snapshot synchronously on the main thread, freezing every client until it completes. Automatic snapshots are controlled by save rules in the config (e.g. save 60 10000 means “snapshot if at least 10000 keys changed in the last 60 seconds”) — Redis checks these rules periodically as part of its normal single-threaded event loop.

AOF (Append Only File) takes the opposite approach: instead of periodic snapshots, it logs every write command as it happens, appending it to a file. Because Redis is single-threaded, each command already executes atomically against memory before anything is written to the AOF buffer — there’s no risk of logging a half-applied write. How often that buffer is actually flushed (fsynced) to disk is controlled by appendfsync: always (fsync after every write — safest, slowest), everysec (fsync once per second — the default, and the recommended middle ground), or no (let the OS decide — fastest, least durable). Because the AOF grows forever as commands accumulate, Redis periodically rewrites it into a compact form via BGREWRITEAOF — another fork+COW operation that regenerates the minimal set of commands needed to reconstruct the current dataset. Since Redis 7, this is done as a multi-part AOF: a base file (which can itself be RDB-formatted for faster loading via aof-use-rdb-preamble) plus an incremental file, tracked by a manifest — this makes rewrites cheaper and loading faster than the old single-file AOF.

The core tradeoff: RDB files are small, fast to load on restart, and cheap to back up — but you can lose everything written since the last snapshot. AOF loses at most one second of writes (with everysec) but produces larger files and a somewhat slower restart, since Redis has to replay the log.

Syntax

There’s no single “persistence command” — instead you interact with persistence through a handful of admin commands and config parameters:

Command Description Time Complexity
SAVE Synchronously writes the dataset to an RDB file, blocking all clients until done. O(N)
BGSAVE Forks a child process to write an RDB snapshot in the background, non-blocking. O(N)
BGREWRITEAOF Forks a child process to rewrite/compact the AOF file in the background. O(N)
LASTSAVE Returns the UNIX timestamp of the last successful RDB save. O(1)
CONFIG GET parameter Reads a live server setting, e.g. save or appendonly. O(N) — N matching parameters
DBSIZE Returns the number of keys in the currently selected database. O(1)
INFO persistence Returns a report of persistence-related status fields and metrics. O(1)

The persistence behavior itself is configured in redis.conf (or via CONFIG SET at runtime), typically something like:

save 3600 1
save 300 100
save 60 10000
appendonly yes
appendfsync everysec
aof-use-rdb-preamble yes

Examples

Example 1 — a manual RDB snapshot with SAVE and checking when it happened:

SET user:1001:name "Ada"
SET user:1001:email "ada@example.com"
DBSIZE
SAVE
LASTSAVE

Output:

OK
OK
(integer) 2
OK
(integer) 1723282400

SAVE writes both keys to dump.rdb synchronously — on a small dataset like this it’s instant, but on a large, busy dataset it would pause every connected client until the write finishes. LASTSAVE then confirms the snapshot’s timestamp; that number is a UNIX epoch time and will be different every time you run it.

Example 2 — checking your current persistence configuration:

CONFIG GET save
CONFIG GET appendonly

Output:

1) "save"
2) "3600 1 300 100 60 10000"
1) "appendonly"
2) "no"

On a default installation, RDB snapshotting is enabled out of the box (three save rules covering different write-volume scenarios) while AOF is off. This is worth checking explicitly before you assume you know how a given Redis instance is protected — many production incidents trace back to someone assuming AOF was on when it wasn’t.

Example 3 — a background snapshot plus inspecting persistence status:

SET session:abc123 "active"
BGSAVE
INFO persistence

Output:

OK
Background saving started
# Persistence
loading:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1723282461
rdb_last_bgsave_status:ok
aof_enabled:0
aof_rewrite_in_progress:0
...(truncated)

BGSAVE returns immediately with “Background saving started” because the actual write happens in a forked child — the main thread was free to accept the next command instantly. INFO persistence is your dashboard for what’s really going on: rdb_changes_since_last_save tells you how much would be lost if the process died right now, and aof_enabled confirms whether the AOF safety net is active at all.

Turning AOF on and forcing a compaction is a runtime configuration change, so it’s shown here for reference rather than as something you’d run against a shared instance carelessly:

CONFIG SET appendonly yes
BGREWRITEAOF

Output:

OK
Background append only file rewriting started

How It Works Step by Step

For an RDB snapshot (BGSAVE): (1) the main thread receives the command and calls fork(); (2) the child process inherits a copy-on-write view of all memory pages — nothing is actually duplicated yet; (3) the child walks every key and serializes it to a temp file; (4) if the parent modifies a page the child hasn’t written yet, the OS duplicates just that page so the child still sees the original data — this is what keeps the snapshot consistent even while writes continue; (5) the child renames the temp file over dump.rdb and exits; (6) the parent, which was never blocked, was serving commands the whole time.

For AOF: (1) a client sends a write command; (2) the main thread executes it against the in-memory dataset — this is the atomic, single-threaded step; (3) the command is appended to an in-memory AOF buffer; (4) depending on appendfsync, that buffer is flushed to disk either immediately (always), once per second by a background thread (everysec), or whenever the OS decides (no); (5) periodically — or when the file grows past a configured threshold — BGREWRITEAOF forks a child that rebuilds a compact base file, exactly like a snapshot, and Redis switches over to appending to the new, smaller file.

Common Mistakes

Mistake 1: Assuming RDB alone means “no data loss.” With only save 60 10000 configured, a crash 59 seconds after the last snapshot loses every write in between. RDB is a snapshot strategy, not a durability guarantee. If your data matters, enable AOF (ideally with appendfsync everysec) alongside RDB rather than relying on snapshots as your only safety net.

Mistake 2: Running SAVE instead of BGSAVE on a production instance. The wrong habit:

SAVE

This blocks every connected client — including your application’s requests — until the entire dataset is written to disk. On a multi-gigabyte dataset that can mean seconds of total unavailability. The fix is almost always the non-blocking version:

BGSAVE

Mistake 3: Setting appendfsync always without understanding the cost. It sounds like the “safest” choice, and durability-wise it is — but it means every single write waits on a disk fsync before Redis considers it complete, which can drop throughput drastically on spinning disks or even some network-attached storage. Unless you have a specific compliance requirement for zero-write-loss, everysec gives you a bounded, small loss window (at most one second) at a fraction of the I/O cost.

Mistake 4: Treating a huge, un-rewritten AOF file as “just how AOF is.” An AOF that never gets compacted grows forever and makes restarts slower, since Redis has to replay the entire log. Rewrites should happen automatically based on growth thresholds, but if you disabled auto-rewrite or the server has been running a very long time without one, trigger it manually and confirm it completed via INFO persistence‘s aof_rewrite_in_progress and aof_last_bgrewrite_status fields.

Best Practices

  • For anything you can’t afford to lose, run RDB and AOF together — RDB gives you fast, compact backups and fast restarts; AOF gives you a tight data-loss window.
  • Default to appendfsync everysec unless you have a hard requirement for zero write loss; reserve always for that specific case and measure the throughput impact first.
  • Keep aof-use-rdb-preamble yes (the Redis 7 default) so AOF rewrites produce a compact RDB-formatted base file instead of a huge command log, speeding up restarts.
  • Monitor rdb_changes_since_last_save, rdb_last_bgsave_status, and aof_last_write_status via INFO persistence — a failed background save or AOF write is silent unless you check for it.
  • Make sure the disk backing your RDB/AOF files has enough free space for a fork’s copy-on-write growth plus the rewritten file — an out-of-disk-space snapshot failure is a common outage cause.
  • Actually test restoring from your persistence files periodically; a backup you’ve never restored isn’t a verified backup.
  • Persistence is not a substitute for replication, and replication is not a substitute for persistence — use replicas for availability and failover, and RDB/AOF for surviving a full outage or data-corruption event.

Practice Exercises

Exercise 1: On a fresh instance, set three keys, run CONFIG GET save to see the active snapshot rules, then run BGSAVE and use INFO persistence to confirm rdb_last_bgsave_status reads ok.

Exercise 2: Compare the behavior of SAVE versus BGSAVE conceptually: write down what a client trying to run GET at the exact moment each one is executing would experience, and why.

Exercise 3: Given a workload that writes 50,000 keys per minute, and a requirement that you can lose at most 2 seconds of writes on a crash, decide: RDB only, AOF only, or both — and which appendfsync setting you’d choose. Justify it in terms of the loss window each option leaves.

Summary

  • RDB creates compact, point-in-time binary snapshots using fork() and copy-on-write, so BGSAVE doesn’t block clients — but data since the last snapshot is lost on crash.
  • AOF logs every write as it happens, fsyncing per the appendfsync policy (always, everysec, or no), trading some performance for a much smaller loss window.
  • SAVE is synchronous and blocks all clients; BGSAVE forks and does not.
  • BGREWRITEAOF compacts the append-only log; since Redis 7 this produces a multi-part AOF (base + incremental + manifest).
  • Neither mechanism alone is a complete durability story for critical data — most production deployments run RDB and AOF together, often alongside replication for availability.
  • Always verify persistence status via INFO persistence rather than assuming it’s configured the way you expect.