Why Redis is Fast

intermediate redis performance internals

Redis routinely does 100,000+ ops/sec on a single core. Compare that to Postgres at maybe a few thousand simple queries on the same hardware. So what’s the magic? It boils down to four things.

1. Everything lives in RAM

Disk reads cost milliseconds. RAM reads cost ~100 nanoseconds. That’s a 10,000x gap. Redis just refuses to touch disk on the hot path — even persistence (RDB snapshots, AOF) happens in the background or in a forked child process.

Think of it like the difference between grabbing a book off your desk versus driving to the library.

2. Single-threaded event loop

This sounds like a weakness — “only one core?!” — but it’s a strength. Single-threaded means no locks, no context switches, no cache coherency drama between cores. Every command runs to completion atomically.

Redis Event Loop (single thread)
Client 1
Client 2
Client N
sockets
epoll / kqueue
multiplexer
Command
Executor
one at a time

Redis uses epoll (Linux) or kqueue (BSD/macOS) to watch thousands of sockets at once. The moment a socket has data, Redis parses the command, runs it, writes the reply, moves on. No thread pools needed.

The only difference from Node.js’s event loop is Redis is written in C, so it’s faster still.

3. Hand-tuned data structures

Redis doesn’t use generic hash tables for everything. It picks the right structure per type, and for small collections it uses ultra-compact encodings:

  • Small hashes → listpack (a packed contiguous array, cache-friendly)
  • Larger hashes → real hashtable
  • Sorted sets → skiplist + hashmap combo (O(log N) range + O(1) lookup)
  • Lists → quicklist (linked list of listpacks)

In simple language: Redis swaps encodings under the hood based on size, so small data is tiny and fast, big data is still O(log N) at worst.

4. Simple wire protocol (RESP)

The Redis Serialization Protocol is plain text with length prefixes. No JSON parsing, no XML, no schema validation. A GET foo is literally:

*2\r\n$3\r\nGET\r\n$3\r\nfoo\r\n

The parser is a few hundred lines of C. Zero overhead.

Pipelining makes it faster still

Network round-trips are the bottleneck once Redis is in-memory. Pipelining sends N commands without waiting for replies:

redis-cli <<EOF
SET a 1
SET b 2
SET c 3
EOF

In a client library:

const pipe = redis.pipeline();
for (let i = 0; i < 1000; i++) pipe.set(`key:${i}`, i);
await pipe.exec();   // one round trip, 1000 commands

The catch

Single-threaded means one slow command blocks everyone. KEYS * on a million keys? Everybody waits. That’s why we use SCAN for production iteration, avoid KEYS/FLUSHALL on live data, and watch out for big LRANGE or HGETALL on huge collections.

Redis 6+ added I/O threads for network reads/writes, but command execution itself is still single-threaded. The mental model holds.