10 min read · Guide · Storage
How it works · Storage · In-memory

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.

Parts01–08 InteractiveLive event loop PrereqSockets / data structures

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.

Command queue · 0
idle
Persistence
RDB snapshot none
AOF · last 12 writes
empty

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.

RDB · snapshot

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.

AOF · log

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.

SLOW COMMANDS

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 STORM

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.

UNBOUNDED

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.



A closing note

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.

Related Caching strategies Cache hit ratio
Found this useful?

Published Updated