redis/redis
Persistence
Active contributors: antirez, Oran Agra, Binbin, Wen Hui.
Redis persists data through two complementary mechanisms — RDB snapshots and the AOF log — that can be used independently or together. The implementation is heavily fork-based: child processes inherit the parent's memory via copy-on-write and serialise the data while the parent keeps serving.
Source layout
| File | Role |
|---|---|
src/rdb.c |
RDB writer and reader. ~4,700 lines. |
src/rdb.h |
RDB opcodes, version constants, public API. |
src/aof.c |
AOF writer, AOF rewriter (forked child), multi-part manifest. |
src/redis-check-rdb.c |
Standalone RDB validator (also linked as a callable function). |
src/redis-check-aof.c |
Standalone AOF validator and --fix mode. |
src/rio.c, src/rio.h |
Buffered IO abstraction used by both formats. Knows about file, fd, buffer, conn. |
src/chk.c, src/chk.h |
Checksum helpers used by RDB and the multi-part AOF manifest. |
src/childinfo.c |
Pipe-based reporting from the persistence child to the parent (memory accounted, COW deltas). |
src/lzf_c.c, src/lzf_d.c |
LZF compression for RDB strings. |
RDB
An RDB file is a snapshot. The writer walks every database, every key, every value, and serialises them into a single binary file with a header (REDIS<version>), a sequence of records, an EOF opcode, and a CRC64 checksum.
When does an RDB get written
| Trigger | Caller |
|---|---|
SAVE |
Synchronously, on the main thread (rare; blocks the server). |
BGSAVE |
Forks a child; the child writes the file and exits. |
save <seconds> <changes> |
The cron checks elapsed time and dirty count and triggers BGSAVE automatically. |
| Replication full sync | Either disk-backed (write file, send file) or diskless (stream straight over the socket). |
SHUTDOWN [SAVE] |
Final RDB before exit. |
Format
The high-level structure is:
- Magic
REDIS+ 4-char version (currently0011for RDB v11). - AUX fields (
redis-ver,redis-bits,ctime,used-mem,aof-preamble, …). - Module-defined data type prologue (per loaded module that registered RDB save callbacks).
- For each database:
SELECT_DBopcode + db id.RESIZEDBopcode + key count + expire count (lets the loader pre-size hash tables).- For each key: optional expire opcode, optional idle/freq opcode, type opcode, key as length-prefixed string, value as type-specific payload.
- EOF opcode.
- CRC64 of everything before.
The opcodes (RDB_OPCODE_*) and type codes (RDB_TYPE_*) are in src/rdb.h. Each version of the format that introduced a new encoding (listpack lists, listpack hashes, listpack sets, stream v2, stream v3, hash with field TTLs, vector sets, …) gets a new type code.
Loading
rdbLoad (in src/rdb.c) is called from the main thread at startup or during DEBUG RELOAD. It pre-sizes the kvstore from the RESIZEDB hint, then deserialises records. Module-registered data types call back via the registered loader.
A failed load aborts startup (a corrupt RDB never silently overwrites the dataset). rdb-check-rdb lets operators verify a file before they roll forward.
AOF
The AOF (Append-Only File) is a write-ahead log of every mutating command. Replay loads the dataset by re-executing the commands in order.
Multi-part AOF (Redis 7+)
A modern AOF is not a single file. It is:
- A manifest file (
appendonly.aof.manifestby default). - One base file — either an RDB (
*.rdb) or a legacy AOF (*.aof). - Zero or more incremental AOF files (
*.aof) chained after the base.
The manifest tracks the names, types, and "incremental sequence" numbers. On rewrite, a new base is generated by the child; once finished, the manifest is atomically renamed to point at the new base, and old files are deleted.
The single-file legacy format is still supported for backwards compatibility (when loading); writes always use multi-part.
Rewrite
BGREWRITEAOF forks a child. The child reads the dataset (in COW memory) and writes a new base file. Meanwhile the parent keeps appending mutations to a separate "rewrite buffer" that the child also reads via a pipe. When the child finishes, the parent atomically swaps the manifest.
Rewrite is automatic when auto-aof-rewrite-percentage % of growth is reached relative to the last rewrite size, and at least auto-aof-rewrite-min-size bytes have been written.
Fsync policy
appendfsync always # fsync after every write — strongest durability, slowest
appendfsync everysec # fsync once per second from a BIO_AOF_FSYNC thread — default
appendfsync no # let the OS decideeverysec lets the BIO thread (src/bio.c) handle the fsync without blocking the main loop. The cron in src/server.c and src/aof.c schedules the fsync; the BIO thread executes it.
Loading
loadAppendOnlyFile reads the manifest, then loads the base (calling rdbLoad if it's an RDB, or replaying commands if it's a legacy AOF), then for each incremental in order, opens it and replays. Replay uses the standard command-table dispatch with a special "fake client" that produces no replies.
A truncated tail in the last incremental file is handled gracefully: the load stops at the last valid command and the rest is discarded with a warning. aof-load-truncated yes (default) controls this.
A corruption mid-file aborts the load. redis-check-aof --fix is the operator escape hatch.
RDB-AOF preamble
aof-use-rdb-preamble yes (default) tells the rewriter to emit an RDB body as the first part of a single-file AOF (legacy mode) or as the base file (multi-part mode). This makes rewrites much faster than re-emitting SET commands and uses less disk.
Failure modes
| Problem | Behaviour | Recovery |
|---|---|---|
| Disk full mid-write | Write is retried; RDB child fails and is restarted next cron. | Free disk; manual BGSAVE/BGREWRITEAOF. |
AOF fsync error with appendfsync always |
The command is rolled back if aof-fsync-on-rewrite no and the write is still in the buffer. Otherwise marked dirty and the server may panic depending on aof-error-mode. |
Fix the disk; resume. |
| Truncated AOF on startup | Loaded up to the last valid command; the rest is ignored. | Optionally re-run --fix. |
| Corrupt RDB on startup | Server refuses to start. | redis-check-rdb, restore from replica, replay AOF. |
Persistence + replication
Replication uses RDB for the full-sync phase. Two modes:
- Disk-backed — the master forks a child, writes the RDB to disk, then ships the file to the replica.
- Diskless — the master forks a child that streams the RDB straight over the replica's socket(s).
For diskless, the master can also stream to multiple replicas at once if they connect in a small time window (repl-diskless-sync-delay). The newer rdbchannel mode opens a second TCP connection so the RDB transfer doesn't block live commands.
See Replication for the protocol details.
Where to start modifying
- Bump the RDB version — add a new
RDB_TYPE_*code insrc/rdb.h, write a saver inrdbSaveObject, write a loader inrdbLoadObject. Be very careful with backward compatibility. - Add an AUX field — write it via
rdbSaveAuxFieldinrdbSaveInfoAuxFields. The loader logs unknown AUX fields and continues. - Tweak rewrite scheduling —
serverCroninsrc/server.chas the AOF/RDB scheduler block. - Improve fsync —
flushAppendOnlyFileinsrc/aof.c. The BIO interface is insrc/bio.c.
Related pages
- Replication — uses RDB for sync.
- Memory management — eviction can drop keys after
LASTSAVE. - Modules —
RedisModule_RegisterDataTyperequires AUX-aware save/load callbacks.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.