How Redis stays fast on a single thread.
A single thread. An event loop. A handful of in-memory data structures. Three persistence modes. Hundreds of thousands of ops per second per box.
Why Redis is single-threaded
One thread, on purpose.
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.
The most surprising fact about Redis is the simplest: there is one thread doing all the command processing. No locks, no atomic ops, no contention. The thread reads from a socket, parses one command, executes it, writes the reply. Then it does the next.
This works because the work each command does is tiny — most are O(1) or O(log n) on small data. A million simple operations per second is achievable on commodity hardware because the path each one walks is short. Multi-threading would add lock contention overhead larger than the work itself.
How Redis processes commands one at a time
Clients queue commands; the single thread runs them in order.
Below: clients send commands; they queue at the server; the single thread pulls one off, runs it, replies. Trigger an RDB snapshot or watch the AOF log fill alongside.
The Redis I/O loop: epoll, parse, execute, reply
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. Then back to the loop.
Newer versions split network I/O onto helper threads (the I/O threads), but command execution stays on one thread. The lock-free design persists. Even when Redis "uses multiple threads," the data-structure operations are single-threaded.
Redis data types: strings, hashes, lists, sets, sorted sets, streams
Each type picks its own encoding as the data grows.
Six core data types, each backed by clever encoding decisions. Small strings live inline (embedded inside the object header — no allocation). Small hashes use ziplists rather than hash tables. Sorted sets are skiplists. The right structure for the size, swapped automatically as data grows.
Each type has its own command vocabulary. LPUSH / RPOP for lists turn it into a queue. ZADD / ZRANGEBYSCORE turn sorted sets into leaderboards. XADD / XREAD make streams a tiny Kafka. The data-structure variety is what makes Redis useful for so many things.
Redis persistence: RDB, AOF, or both
How an in-memory store survives a restart.
Redis is in-memory, but it can survive restarts. 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 actually copied until written. Compact, fast to load on restart, but you can lose up to N minutes on crash.
Every command, appended.
Every write command is appended to a log file. Recovery replays the log — the same write-ahead-log idea every durable store uses. fsync policy (always / every-second / never) trades durability for throughput. AOF rewrites compact the log periodically. Modern default: AOF + RDB together — RDB for fast recovery, AOF for low data-loss windows.
Redis eviction: what gets dropped when memory runs out
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. Eight policies in modern Redis — LRU, LFU, random, TTL-based — variants for "all keys" vs "keys with 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. Always set a policy explicitly — 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, two scaling shapes: replication for read scale-out, and cluster for write scale-out.
Replication is asynchronous by default — every write is also streamed to replicas. Replicas can serve reads. A primary failure triggers Sentinel-driven failover; one replica is promoted, the rest re-attach.
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 have to stay within a single slot — use hash tags ({user:1}:profile) to force keys onto the same node.
Where Redis earns its scars in production
The three ways teams melt a Redis instance.
One slow command blocks all
Single thread means a slow command (KEYS *, LRANGE on a huge list, expensive SCRIPT) blocks every other client. Use SCAN, never KEYS; bound LRANGE; profile 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 TTL by default; use EXPIRE on every SET; pair with an eviction policy. Trust nothing; cap everything.
Redis in production: Twitter, Instagram, GitHub, Pinterest
Real workloads.
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 choice was deliberate: the session-write rate is too high for Postgres, too important to lose on a single-node failure. Their 2018 incident postmortem on a 24-hour outage walks through how cross-region Redis replication recovered.
Pinterest — pin counts and rate limiting. Pinterest uses Redis for atomic counter increments (likes, repins) and as a distributed rate limiter. They publicly contributed Redis Cluster proxy improvements upstream.
The single-machine throughput. A single Redis instance hits ~150,000 ops/sec on commodity hardware (single-thread bound). Redis 6's I/O threads brought that to ~400,000. Beyond that, you need Cluster — but cross-shard transactions don't work across slots, so the schema design has to keep related keys on the same hash tag.
Redis is fast because it does less. One thread, in-memory, simple data structures, optional durability. The contract has held for fifteen years across thousands of production deployments. Use it as a cache, a queue, a leaderboard, a session store, a tiny stream processor — not all of them at once on one box.
Further reading on Redis internals
Primary sources, in order.
- Redis docsInternalsAuthoritative architecture overview — event loop, persistence, cluster.
- Redis docsData typesEach type, each command, with complexity. The single most useful Redis reference.
- Semicolony guideHTTP cachingWhere Redis sits in the cache hierarchy — the app-tier cache that pairs with browser and CDN.
- Semicolony simulatorLRU cacheWatch the eviction policy in action — what allkeys-lru does on a synthetic workload.