Redis offers both a `SAVE` and a `BGSAVE` command for writing a snapshot to disk. What is the difference between them, and why is one of them effectively forbidden on a production server?
answer
- SAVE = blocks the single thread, outage
- BGSAVE = fork + copy-on-write child
- only pause is fork, see latest_fork_usec
- temp file then atomic rename
- MISCONF: failed bgsave can block writes
basics
~20 sSAVE writes the snapshot on the main thread, so the server answers no commands until it finishes — unusable in production. BGSAVE forks a child that writes the snapshot while the parent keeps serving; only the brief fork itself pauses the server.
solid answer
~50 sBoth produce the same `dump.rdb` file; they differ in who does the work. `SAVE` serializes the dataset **on the main thread**. Redis executes commands one at a time on a single thread, so for the whole duration — seconds to minutes on a large dataset — every client is blocked and every request queues. On a live server that is an outage. `BGSAVE` calls `fork()`. The child inherits a copy-on-write view of memory and writes the snapshot from it, while the parent immediately goes back to serving commands. The only stall is the fork itself, whose cost scales with the size of the process's page tables — typically milliseconds to tens of milliseconds per gigabyte, and worse on virtualized hardware. Automatic save points always use `BGSAVE`. `SAVE` survives for scripted maintenance where the server is already quiet and you want to be certain the file is complete before the process exits.
code
text · 9 lines$ redis-cli LASTSAVE
(integer) 1723640112
$ redis-cli BGSAVE
Background saving started
$ redis-cli INFO persistence | grep -E 'rdb_bgsave_in_progress|rdb_last_bgsave_status'
rdb_bgsave_in_progress:0
rdb_last_bgsave_status:ok
$ redis-cli LASTSAVE # advanced => the new snapshot is on disk
(integer) 1723640119go deeper
State clearly that SAVE blocks every client until it finishes and BGSAVE forks a child so the server keeps serving, and that automatic saves use BGSAVE.
Add the mechanism: fork, copy-on-write child, temp file plus atomic rename, and the fork pause as the one real stall.
Bring in latest_fork_usec, copy-on-write memory growth versus write rate, the single-child restriction, and MISCONF write rejection after a failed save.
Discuss when snapshotting belongs on the primary at all — moving it to a replica, sizing memory headroom for copy-on-write, and treating fork pauses as part of the instance's latency budget.
## What a snapshot is An RDB snapshot is a point-in-time binary image of the entire dataset — every key, its value in the internal encoding, and its expiration — written to the file named by `dbfilename` inside `dir` (by default `dump.rdb`). It is compact, optionally LZF-compressed, and carries a CRC64 checksum so a truncated or corrupted file is detected on load. Redis has two commands to produce one on demand, plus automatic save points that use the background form. ## `SAVE`: synchronous, on the main thread Redis processes client commands on a single thread. `SAVE` performs the serialization inline on that thread. Until the last byte is written, the server executes nothing else: clients block, the accept queue backs up, health checks time out, and any monitoring that speaks Redis reports the instance as down. On a 10 GB dataset that is not a hiccup, it is an outage of tens of seconds or more. The upside of that same property is determinism: when `SAVE` returns `OK`, the file on disk is complete and no further writes have been applied. That makes it occasionally useful in a maintenance script on an instance that is already drained — for instance, before deliberately killing a process on a box you are decommissioning. Redis also performs a blocking save on `SHUTDOWN` when save points are configured, for exactly this reason. ## `BGSAVE`: fork a child `BGSAVE` returns immediately with `Background saving started`. Under the hood Redis calls `fork()`. The child process gets a logically identical copy of the parent's memory, implemented by the kernel as **copy-on-write**: nothing is duplicated up front, page tables are marked read-only, and a page is only copied when one side writes to it. The child then walks that frozen view and writes the RDB to a temporary file; on success it renames the temp file over `dump.rdb`, which is atomic on the same filesystem, so a crash mid-save leaves the *previous* snapshot intact. The parent, meanwhile, keeps serving. The one unavoidable pause is `fork()` itself, which must copy the page tables — cost proportional to how much memory the process maps, commonly on the order of ten-ish milliseconds per gigabyte on bare metal and noticeably worse on some virtualized instances. `INFO stats` reports the last one as `latest_fork_usec`, and it is one of the first things to check when a user reports a periodic latency spike. The less obvious cost is memory: because the parent keeps taking writes, every page it modifies during the save gets copied, so resident memory grows during the snapshot in proportion to the write rate and the save duration. ## Concurrency and status Only one child of this kind runs at a time. If a `BGSAVE` is already running, another `BGSAVE` is refused; if an AOF rewrite is running, Redis will typically defer a save point rather than fork twice. Useful fields from `INFO persistence`: - `rdb_bgsave_in_progress` — 1 while a child is writing - `rdb_last_bgsave_status` — `ok` or `err` - `rdb_last_save_time` — Unix time of the last successful save (also returned by `LASTSAVE`) - `rdb_changes_since_last_save` — how many writes are currently unprotected A scripted snapshot usually reads `LASTSAVE`, issues `BGSAVE`, and polls `LASTSAVE` until it advances — that is the non-blocking equivalent of waiting for `SAVE` to return. ## Failure behavior worth knowing If the background save fails — full disk, permissions, fork denied for lack of memory — `rdb_last_bgsave_status` becomes `err`, and with the default `stop-writes-on-bgsave-error yes` Redis begins **rejecting write commands** with a `MISCONF` error. This surprises people badly: the instance is up, reads work, writes fail. It is deliberate, on the reasoning that a server configured to persist should tell you loudly when it no longer can. On a pure cache, where persistence is a nicety, setting it to `no` is defensible; on a store of record, fix the disk instead. ## Summary line for an interview Same output, different execution: `SAVE` blocks the single command thread for the whole write; `BGSAVE` forks and writes from a copy-on-write child, paying only the fork pause plus copy-on-write memory. Automatic save points use `BGSAVE`; `SAVE` belongs in maintenance scripts on quiet instances.
- If `BGSAVE` does not block the server, what is the cost of running it?Three things. The `fork()` call itself pauses the main thread for a time proportional to the process's page tables (visible as `latest_fork_usec`). Copy-on-write means memory grows during the save in proportion to the write rate, so a write-heavy instance can approach double its resident size. And the child competes for disk I/O and CPU, which can add latency on a saturated box.
- How does a script know a background save actually finished successfully?Record `LASTSAVE` before issuing `BGSAVE`, then poll `LASTSAVE` until the timestamp advances — it only moves on a successful save. Cross-check `rdb_last_bgsave_status:ok` and `rdb_bgsave_in_progress:0` in `INFO persistence`. Do not assume success just because `BGSAVE` returned, since it returns as soon as the child is forked.
- What happens to the existing dump.rdb if the server is killed while a background save is running?It survives untouched. The child writes to a temporary file and only renames it over `dump.rdb` after writing the complete image, and the rename is atomic within a filesystem. A crash mid-save therefore leaves you with the previous complete snapshot rather than a half-written one.
SAVE is closing the shop to take inventory; BGSAVE is photographing the shelves and counting from the photo while customers keep shopping.
saying these in an interview costs you the question
- Claiming `BGSAVE` runs on a background thread — it is a forked process, not a thread
- Saying `SAVE` is fine because it is fast on a small dataset, without acknowledging it blocks all clients
- Thinking `BGSAVE` has no cost at all on the main thread (the fork pause is real)
- Assuming the child sees writes made after the fork — the snapshot is the state at fork time
- Not knowing that a failed background save can start rejecting writes with MISCONF