A point-in-time dump of the entire state, written atomically. Combined with the AOF from step 5, recovery becomes "load the snapshot, then replay the deltas".
make 06-rdb-snapshots
redis-cli -p 6380 SET foo bar
redis-cli -p 6380 SET counter 1
redis-cli -p 6380 SAVE
cat 06-rdb-snapshots/data.rdb
# {"version":1,"saved_at":1737000000.0,"store":{"foo":"bar","counter":"1"},"expires":{}}| Command | Effect |
|---|---|
SAVE |
Blocking dump to data.rdb |
BGSAVE |
Fork a child to do the save in the background |
LASTSAVE |
Unix timestamp of the most recent successful save |
Both. On startup the server loads RDB first, then replays the AOF. The AOF can be much smaller because the RDB took care of the bulk.
In real Redis, the RDB is a custom binary format with checksums, length encoding for small ints, and string interning. We use JSON here because the lesson is about WHEN to snapshot, not about format optimisation.
rdb_save writes to data.rdb.tmp then renames it to data.rdb. The rename is atomic on POSIX file systems. A reader either sees the old file or the new file, never a half-written one. The same trick is what makes config-file writes safe.
os.fork() duplicates the process. The child inherits the dicts via copy-on-write: same physical memory pages until either side mutates a page. The child can take its time writing the snapshot while the parent keeps serving traffic. Real Redis does exactly this.
Caveat: copy-on-write is undone if the parent writes a lot during the save (every dirty page gets copied). In Redis production, BGSAVE during heavy write traffic can briefly double the memory footprint.
Step 7 adds Pub/Sub. The server starts tracking which clients want which channels, and PUBLISH fans out a message to all of them.