Why Redis Is Fast — Memory, a Single Thread, and Data Structures
The API diagnosis post mentioned Redis as the usual cache store. But why is Redis fast? “Because it’s in memory” is half the answer — plenty of memory-based systems are slower. This post lays out the three pillars of Redis’s speed (memory, a single thread, data structures) and the price that design demands. Know the mechanics and you also get the diagnosis for “Redis got slow.”
Pillar ① memory — no disk in the path #
A regular database sends writes to disk for durability, and reads go to disk on cache misses. As the SSD post showed, that path costs microseconds to milliseconds. Redis keeps all data in memory, so the request path contains no disk at all. Memory access takes nanoseconds, while disk access takes microseconds to milliseconds — two to three orders of magnitude apart.
Disk didn’t vanish entirely: persistence (snapshots, AOF) exists, but pushed out of the path, where it can’t hold responses hostage. How snapshots run without stopping the service — fork plus copy-on-write — was covered in the fork post. Half of being fast is deciding what to move out of the path; that’s pillar one’s lesson.
Pillar ② a single thread — no locks, but one line #
The core that executes commands is a single-threaded event loop. Requests from tens of thousands of clients form one line, processed in order — but each takes microseconds, so hundreds of thousands clear per second. The rationale for this counterintuitive choice is the cost of shared memory from the process/thread post.
- No locks needed. No two threads touch data simultaneously, so no contention, no deadlocks, no time spent waiting on locks. The time multithreaded systems lose to locking even as they add cores, Redis never pays.
- Every command is atomic. INCR is a race-free counter as-is; the same serial nature is why Redis is a favorite for distributed locks.
- CPU wasn’t the bottleneck anyway. Redis work is mostly memory reads and writes; one core saturates the network first. Tellingly, recent versions added threads for network I/O (buffer handling), not command execution. Spare cores? Run more instances — that’s Redis-style multicore.
The price is equally clear: one line means one slow command stalls everyone. A KEYS * over a million keys, a full read of a giant collection, or deleting a huge key that takes 100ms makes every queued request wait 100ms with it. It’s the classic cause of “Redis suddenly got slow,” and the fixes are SCAN instead of KEYS, UNLINK (background deletion) for big deletes, and SLOWLOG to identify the offender.
Pillar ③ data structures — no conversion cost #
Store a “recent items list” in a relational database and you convert into tables and indexes, then convert back with sorting and parsing at read time. Redis holds lists, hashes, sets, and sorted sets in their native shape in memory, with the operations each use case needs already optimized.
- A leaderboard is a sorted set’s “keep sorted by score + rank lookup” — the answer in itself. What ORDER BY recomputes every time, the structure maintains continuously.
- Counters are one INCR; recent-N lists are LPUSH + LTRIM; deduplication is a set.
What’s fast in a “fast store” depends on structure choice, so using Redis well is largely choosing structures well. Stuff everything into JSON strings and parse whole blobs each read, and you’ve thrown pillar three away.
When it’s slow anyway — the diagnosis list #
Invert the pillars and the checklist writes itself. If Redis is slow, in order:
- Slow commands —
SLOWLOG GET. KEYS, whole-collection reads, and big-key deletions are the regulars. - Memory ceiling — at maxmemory, eviction work piggybacks on requests; pushed to swap, your memory system becomes a disk system (the swap story from the memory post applies verbatim). Check eviction policy and the memory trend.
- Persistence costs — fork-moment latency on snapshots, and the AOF fsync setting (
alwaysputs the disk back in the path). - Network round trips — a command is microseconds; a round trip is milliseconds. Calling GET a thousand times in a loop is the round-trip multiplication problem; pipelines and MGET are the fix.
Summary #
- Redis’s speed is the product of memory (no disk in the path), a single thread (no locks, atomic commands), and native data structures (no conversion).
- The single-thread price: one slow command stalls all. SCAN over KEYS, UNLINK for big deletes, SLOWLOG for diagnosis.
- Structure choice is performance: sorted sets for rankings, INCR for counters — pick right or lose pillar three.
- When slow: slow commands → memory/eviction → persistence settings → round-trip counts, in that order.
- On the application side, batching round trips (pipelines, MGET) comes before any server tuning.