Redis's Single-Threaded Event Loop — Speed, Limits, and Multithreading8 min read
Module F-4·8 min read
Why Redis is fast despite being single-threaded, the event loop model in plain terms, and the one rule to never break — KEYS * blocks every client. Production tuning and the full failure-mode playbook continue in A-14.
Who this module is for: You know Redis is fast, but you're not sure why it's fast, or how a single-threaded process can handle millions of requests per second. This module covers the core I/O multiplexing model and when a single thread becomes a bottleneck.
More Threads Isn't Automatically Faster
If you've worked with a database or web server before, your instinct probably says: more threads, more CPU cores, more work done in parallel, more throughput. Redis breaks that intuition on purpose.
Think of a small coffee shop with one incredibly fast barista instead of three average ones. Three baristas sound faster — until they all need to reach into the same till at once, tell each other who's making what, and occasionally bump into each other at the espresso machine. One barista who already knows exactly where everything is, moving without ever needing to check in with anyone, can outpace all three combined for high-volume, simple orders.
That's Redis. Almost every command it runs — GET, SET, HSET, ZADD — is a handful of in-memory pointer lookups. There is no disk seek to wait for, no complex computation to perform. The actual "work" per command takes a fraction of a microsecond. The one barista is plenty.
The Event Loop, in Plain Terms
So if one thread does all the work, how does Redis serve 100,000+ requests per second without clients queuing up behind each other for whole seconds at a time?
The trick is that Redis's single thread never sits around waiting on any one client. It uses an operating system mechanism (epoll on Linux, kqueue on macOS) that lets it watch thousands of client connections at once and get tapped on the shoulder the instant any of them has something ready to read:
text
Nobody is idle-waiting for anybody else. The thread just keeps cycling through whichever connections have data ready, executes each command fully, and moves to the next — all before a human would even perceive a delay.
What This Buys You: Atomicity for Free
Because only one command can ever be "in progress" at a time, there is no window where two clients can interfere with the same key mid-operation. INCR, HINCRBY (which you used in F-3 for per-user counters), ZADD — every one of these is atomic by construction, with no locks anywhere in the picture. You never have to reason about race conditions the way you would with two threads reading and writing the same variable in most programming languages.
This is the real payoff of the single-threaded design: correctness, not just speed.
The One Rule You Must Never Break
Here's the catch. Since every command runs to completion before the next one starts, a command that takes 200ms to finish makes every other client wait 200ms — no matter how simple their own request was.
The classic way to trip this wire is KEYS *. On a Redis instance with a few million keys, KEYS * has to walk the entire keyspace before it can return anything — and while it's walking, Redis answers nothing else.
text
As you saw back in F-1, SCAN exists specifically to avoid this: it returns a small batch and a cursor instead of everything at once, so Redis can keep serving other clients between each call. The one habit worth internalizing right now: if a command could touch an unbounded number of keys, don't run it directly against production.
A Quick Note on Redis 6+ and "Multithreading"
You may have heard that modern Redis (6.0+) added multithreading and wondered if any of the above still applies. It does. Redis 6 added extra threads only for reading bytes off the network and writing responses back — the actual execution of your commands is still funneled through the single main thread, so the atomicity guarantee above never changes.
We'll come back to exactly what that buys you, how to configure it, and the full list of commands to watch out for in production, in A-14.
Summary
Redis is I/O-bound, not CPU-bound — one thread with I/O multiplexing outruns a multi-threaded design for its typical workload.
The event loop watches thousands of connections at once and executes one command fully before moving to the next.
Because commands never interleave, operations like INCR and HINCRBY are atomic with zero locking.
A single slow, unbounded command (the canonical example: KEYS *) blocks every other client for as long as it runs — use SCAN instead.
Redis 6+'s "I/O threads" only parallelize reading/writing sockets, not command execution — the single-threaded guarantee holds.
Next: F-5 — TTL, Expiry, and Eviction — how Redis manages key lifetimes, the seven eviction policies, and how to design your keyspace so you never run out of memory unexpectedly.
Knowledge Check
How does Redis achieve such high throughput despite being primarily single-threaded?
When can Redis's single-threaded nature become a noticeable bottleneck in production?
While the core execution engine is single-threaded, does Redis use background threads for any operations?
Test your knowledge with more question sets
Sign in to access a wider variety of questions and get notified when new practice sets are added to this module.
Client A: GET user:1001
Client B: SET counter 42
Client C: HGETALL session:abc
Redis (single thread, one at a time):
1. GET user:1001 → answer A
2. SET counter 42 → answer B
3. HGETALL session:abc → answer C
127.0.0.1:6379> KEYS *
(... the entire server pauses until this finishes ...)