How Redis stays fast on a single thread.
Redis runs commands on a single thread driven by an event loop, keeps its data structures in memory, and can persist them with RDB snapshots, an AOF log, or both. A single instance handles hundreds of thousands of ops per second.
Why Redis is single-threaded
A deliberate single-thread design.
Redis is a single-threaded, in-memory data structure server. Salvatore Sanfilippo released it in 2009; the single-threaded design, one event loop processing all commands serially, is the source of its predictable latency and operational simplicity. Redis ships strings, hashes, lists, sets, sorted sets, streams, bitmaps, hyperloglogs, and geospatial indexes as primitive types.
Redis processes all commands on one thread, so command execution needs no locks or atomic operations and has no contention. The thread reads from a socket, parses one command, executes it and writes the reply, then moves on to the next.
This works because each command does very little: most are O(1) or O(log n) on small data. The code path for each one is short, so a million simple operations per second is achievable on commodity hardware. Multi-threading would add lock overhead larger than the work itself.
How Redis processes commands one at a time
Clients queue commands; the single thread runs them in order.
In the simulator below, clients send commands that queue at the server, and the single thread takes one at a time, runs it and replies. You can also trigger an RDB snapshot and watch the AOF log fill alongside.
The Redis I/O loop: epoll, parse, execute, reply
Redis uses a Reactor pattern: one thread, one event loop on epoll/kqueue. The loop blocks waiting for any connected client socket to be readable. When one is, the thread parses one or more commands from it, runs them, and writes replies. It then returns to the loop.
Newer versions split network I/O onto helper threads (the I/O threads), but command execution stays on one thread. Even when Redis uses multiple threads, the data-structure operations stay single-threaded.
Redis data types: strings, hashes, lists, sets, sorted sets, streams
Each type picks its own encoding as the data grows.
Redis has six core data types, and each picks a compact encoding for small values. Small strings are embedded in the object header, which avoids a separate allocation. Small hashes use ziplists rather than hash tables, and larger sorted sets use skiplists. Redis switches encodings automatically as the data grows.
Each type has its own command vocabulary. LPUSH / RPOP for lists turn a list into a queue. ZADD / ZRANGEBYSCORE turn sorted sets into leaderboards. XADD / XREAD make streams a small Kafka-like log. This variety of data structures is why Redis fits so many uses.
Redis persistence: RDB, AOF, or both
How an in-memory store survives a restart.
Redis keeps its data in memory but can survive restarts through two persistence mechanisms, often used together.
Periodic full dumps
Every N minutes (and after M writes), Redis forks a child process. The child traverses memory and writes a binary snapshot to disk. The parent keeps serving requests; CoW (copy-on-write) means most pages aren't copied until written. Compact, fast to load on restart, but you can lose up to N minutes on crash.
Every write appended to a log
Every write command is appended to a log file. Recovery replays the log, which is the write-ahead-log idea used by most durable stores. fsync policy (always / every-second / never) trades durability for throughput. AOF rewrites compact the log periodically. A common setup is AOF and RDB together, with RDB for fast recovery and AOF for a small data-loss window.
Redis eviction: what gets dropped when memory runs out
Redis is bounded by RAM. The maxmemory setting caps usage; maxmemory-policy decides what to drop when the cap is hit. Redis has eight policies (LRU, LFU, random and TTL-based), with variants for all keys or only keys with a TTL set. (See the caching guide for the wider hierarchy.)
The most common production choice is allkeys-lru: when full, drop the least-recently-used key. For workloads with mixed temperature, allkeys-lfu (least-frequently-used) does better. Set a policy explicitly, because the default noeviction rejects writes once memory is full, which is rarely what you want.
Redis replication and clustering
Replicas scale reads; cluster shards scale writes.
A single Redis primary can serve hundreds of thousands of QPS. Beyond that there are two ways to scale: replication for reads and cluster for writes.
Replication is asynchronous by default: every write is also streamed to replicas. Replicas can serve reads. If the primary fails, Sentinel triggers a failover: one replica is promoted and the rest re-attach to it.
Cluster shards the keyspace across N primaries via consistent-hash slots (0..16383). Each primary owns a slot range; clients route by hashing the key. Multi-key commands must stay within a single slot, so use hash tags ({user:1}:profile) to put related keys on the same node.
Common Redis failures in production
Three common ways a Redis instance falls over.
One slow command blocks all
Because there is one thread, a slow command (KEYS *, LRANGE on a huge list, an expensive script) blocks every other client. Use SCAN instead of KEYS, bound LRANGE, and check the slowlog.
Fork stalls during snapshots
RDB snapshots fork the process; on a multi-GB instance, the fork can stall the parent for hundreds of milliseconds. Schedule snapshots off-peak or use AOF-only.
No TTL means eventual OOM
Caches without TTLs accumulate forever. Set a TTL on every key (for example with EXPIRE after each SET) and pair it with an eviction policy.
Redis in production: Twitter, Instagram, GitHub, Pinterest
Workloads from four companies.
Twitter: timeline cache. Twitter caches user timelines in Redis (sorted sets keyed by user, indexed by tweet ID). Each fan-out write inserts into the followers' timeline sorted sets; reads fetch the top N. The timeline service handles tens of millions of QPS off Redis instances; Postgres / Manhattan handle the durable storage.
Instagram: feed metadata. Instagram's media-metadata cache runs on Redis cluster across thousands of nodes. The hot-path cache hit ratio is reported >99.5%; behind the cache sits Cassandra. Public posts (2020) document the architecture and the years-long migration from a single Redis instance to a sharded cluster.
GitHub: session storage. GitHub keeps user-session state in Redis with replication for failover. The session-write rate is too high for Postgres, and the data is too important to lose on a single-node failure.
Pinterest: pin counts and rate limiting. Pinterest uses Redis for atomic counter increments (likes, repins) and as a distributed rate limiter. They have also published their work on Redis Cluster proxying.
Single-machine throughput. A single Redis instance reaches ~150,000 ops/sec on commodity hardware, bound by the single thread, and Redis 6's I/O threads raise that to ~400,000. Beyond that you need Cluster, where multi-key operations only work within one slot, so the schema has to keep related keys under the same hash tag.
Redis is fast because it does little per command: one thread, in-memory data, simple data structures and optional durability. That design has held up for over fifteen years in production. It works as a cache, a queue, a leaderboard, a session store or a small stream processor, though running all of those on one instance is a bad idea.
Further reading on Redis internals
Primary sources, in order.
- Redis docsInternalsArchitecture overview covering the event loop, persistence and cluster.
- Redis docsData typesEvery type and command, with complexity. The most useful Redis reference.
- Semicolony guideHTTP cachingWhere Redis sits in the cache hierarchy, as the app-tier cache alongside browser and CDN caches.
- Semicolony simulatorLRU cacheThe eviction policy in action: what allkeys-lru does on a synthetic workload.