Single-Threaded Event Loop
Redis processes all client commands on a single main thread sequentially. This design eliminates the need for locking on core data structures — there is no concurrent mutation, so no mutexes are required. The event loop is built on I/O multiplexing via epoll (Linux), kqueue (BSD/macOS), or select as a fallback. The main thread blocks on epoll waiting for readable/writable file descriptors, then dispatches commands one at a time.
Since Redis 6, I/O threads handle network reads and writes in parallel while the main thread still executes all commands. Background threads handle persistence (AOF fsync, RDB child) and lazyfree (async key deletion). The single-threaded execution model means that a slow command (e.g., KEYS *, LRANGE on a huge list) blocks all other clients — this is why Redis exposes non-blocking alternatives like SCAN.
Data Structure Implementations
Redis implements each data type with carefully chosen internal representations optimized for both memory and CPU:
- String: uses SDS (Simple Dynamic String) — a struct with
len,alloc, andbuf[]. Unlike C strings, SDS tracks length explicitly, makingstrlenO(1) and allowing binary-safe storage (embedded NUL bytes). Small integers are stored as actual integer types to save memory. - List: implemented as a quicklist — a doubly-linked list where each node is a listpack (compact, length-prefixed encoding). This gives O(1) push/pop at both ends with good memory locality for moderate-sized elements.
- Hash: uses listpack for small hashes (below
hash-max-listpack-entriesandhash-max-listpack-valuethresholds), upgrading to a hashtable with chaining for large hashes. The hashtable uses incremental rehashing — two hash tables coexist during resize, with entries migrated on each access. - Set: uses intset (sorted integer array) when all elements are integers and the count is small, upgrading to a hashtable otherwise.
- Sorted Set (ZSet): uses listpack for small sets, upgrading to a combined skiplist + hashtable. The hashtable gives O(1) score lookup by member. The skiplist gives O(log N) rank queries, range-by-score, and range-by-rank operations.
Skiplist: The Sorted Set Engine
The skiplist is a probabilistic data structure with multiple levels of linked lists. Level 0 is a full sorted linked list; higher levels skip over increasing numbers of nodes. On average, each node participates in O(log N) levels. Insertions and lookups are O(log N). Redis chose skiplist over a balanced BST (like AVL or red-black tree) because skiplist range operations are simpler to implement and the memory overhead is comparable. The combined skiplist+hashtable allows ZSETs to serve both ZSCORE (O(1) via hashtable) and ZRANGE (O(log N + M) via skiplist) efficiently.
Memory Encoding: Listpack
Listpack (replacing the older ziplist in Redis 7.0+) is a compact, contiguous memory encoding for small collections. Each entry stores: a "previous entry length" field (for reverse traversal), an encoding byte (type + length), and the actual data. There are no pointers — entries are packed end-to-end. This saves significant memory for small lists, hashes, and sorted sets compared to pointer-based structures. The tradeoff is O(N) access by index, which is acceptable for small collections.
RDB Snapshots
RDB (Redis Database) persistence writes a compact binary snapshot of the entire dataset. When BGSAVE is triggered, Redis calls fork() to create a child process. The child serializes all data to a temp file using a compact binary format, then atomically renames it to dump.rdb. The parent continues serving clients uninterrupted.
Copy-on-Write (COW): immediately after fork, parent and child share the same physical memory pages. When the parent modifies a page, the OS copies that page for the parent — the child still sees the original. This means memory usage can temporarily spike by up to 2x during BGSAVE if writes are heavy. RDB files load fast on restart, but data written after the last snapshot is lost on crash.
AOF: Append-Only File
AOF logs every write command as it is executed, in Redis serialization protocol format. On restart, Redis replays the AOF to reconstruct state. Three fsync policies control durability vs. performance:
- always: fsync after every command. Maximum durability, significant performance cost.
- everysec (default): fsync every second via a background thread. At most 1 second of data loss on crash.
- no: let the OS decide when to flush. Fastest, but data loss window equals OS flush interval (typically 30s).
AOF Rewrite: over time the AOF grows large with redundant commands (e.g., 1000 INCR commands on a key that could be represented as one SET). Background AOF rewrite compacts the file by writing the current state as a minimal set of commands. Redis forks a child to write the new AOF, while an AOF rewrite buffer captures new writes. On completion, the buffer is appended and the file is atomically swapped.
Replication
Redis uses asynchronous master-replica replication. On initial sync, the master runs BGSAVE and sends the RDB file to the replica; the replica loads it and then receives a backlog of buffered write commands. Ongoing replication streams every write command from master to replicas in real time.
The PSYNC protocol supports partial resynchronization: each master tracks a replication offset and a replication ID. If a replica disconnects briefly, it reconnects and sends its last known offset. If the master’s backlog still contains the missing commands, it sends only the delta — avoiding a full resync. Replicas are eventually consistent: a write acknowledged by the master may not yet be on replicas.
Redis Sentinel
Sentinel provides high availability for a single-master Redis setup. Sentinel processes monitor master and replica health via periodic PING commands. When a master is unreachable, Sentinels vote among themselves (quorum) to confirm the failure (avoiding split-brain). The elected Sentinel orchestrates failover: promotes a replica to master, reconfigures remaining replicas to replicate from the new master, and publishes the new master address. Clients use Sentinel to discover the current master address at connection time.
Redis Cluster
Redis Cluster provides horizontal sharding across multiple nodes. The keyspace is divided into 16,384 hash slots. Each key is mapped to a slot via CRC16(key) mod 16384. Each master node owns a subset of slots; its replicas hold copies. Clients can connect to any node — if a key belongs to a different node, the client receives a MOVED redirect.
Cluster nodes discover each other and propagate state via a gossip protocol — each node periodically pings random peers, exchanging cluster membership and slot assignment information. Resharding (moving slots between nodes) can happen without downtime using CLUSTER SETSLOT commands. Hash tags (e.g., {user}.session) force keys to the same slot, enabling multi-key commands on co-located keys.
Memory Optimization and Eviction
When Redis reaches maxmemory, it applies an eviction policy to free space:
- noeviction: return errors on writes (default).
- allkeys-lru / volatile-lru: evict least-recently-used keys (all keys or only keys with TTL).
- allkeys-lfu / volatile-lfu: evict least-frequently-used keys (Redis 4.0+). LFU uses a logarithmic counter with decay to estimate frequency.
- allkeys-random / volatile-random: random eviction.
- volatile-ttl: evict keys with shortest remaining TTL first.
LRU and LFU in Redis are approximate — Redis samples a small number of keys (configurable via maxmemory-samples) and evicts the best candidate from the sample rather than maintaining a full LRU list, keeping overhead low.
Lua Scripting
Redis embeds a Lua interpreter (via the EVAL command) that executes scripts atomically on the server. While a Lua script runs, no other command can execute — the event loop is blocked. This enables complex check-and-set operations and multi-step atomic workflows without WATCH/MULTI/EXEC transaction boilerplate. Scripts are cached on the server by their SHA1 hash (EVALSHA) to avoid re-sending script text on every call. Scripts can call any Redis command via redis.call() or redis.pcall() (the latter traps errors rather than raising them). Since Redis 7.0, Redis Functions (stored server-side Lua or other engine scripts) replace ad-hoc EVAL scripts for production use.
See also: Meta Interview Guide 2026: Facebook, Instagram, WhatsApp Engineering
See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering
See also: Anthropic Interview Guide 2026: Process, Questions, and AI Safety