Skip to content
Back to blog

Why Is Redis So Fast? Six Design Decisions Explained

July 5, 20268 min read

Redis routinely serves sub-millisecond reads while your PostgreSQL query is still waking up. That speed is not magic — and it is not one clever trick. Redis is fast because every layer of the stack is designed to eliminate unnecessary work: RAM instead of disk, a single execution thread instead of lock contention, purpose-built data structures instead of generic blobs, and an event loop that handles thousands of connections without spawning a thread per client.

This article breaks down the six architectural reasons behind Redis performance — the same pillars shown in every "why Redis?" explainer — so you know what you are buying when you add it to your stack, and what trade-offs come with it.

Redis — speed by designIn-Memory1All data in RAMSingle-Thread2No lock contentionData Structures3SDS, skip listsO(1) Commands4GET, SET, HGETEvent Loop5epoll / kqueueCompact Encode6Small → scale up
Six reasons Redis is fast — speed by design

1. In-Memory Storage

All Redis data lives in RAM. Reads and writes never wait on spinning disks or SSD seek latency for the hot path — the operating system pages memory directly into the process address space.

That is the biggest latency win and the biggest constraint. RAM is expensive and finite; datasets that outgrow memory need eviction policies (LRU, LFU, TTL) or tiering strategies (Redis on Flash, external persistence for cold data). Persistence (RDB snapshots, AOF append-only files) writes to disk asynchronously — it does not block normal GET/SET on the hot path.

Compare to a disk-backed database: even with a buffer pool, a cache miss may touch storage. Redis assumes you are OK keeping working set in memory and accept that restart without persistence loses data unless you configure otherwise.

Quick reference

  • Typical latency: sub-millisecond for simple commands vs milliseconds–tens of ms for disk-backed DBs.
  • Working set must fit in memory (or use Redis Enterprise / tiered options for overflow).
  • RDB: point-in-time snapshots; AOF: replay log — both optional durability trade-offs.
  • Use case fit: hot keys, sessions, rate limits, leaderboards — not your only copy of critical data without backup.
  • Memory cost: plan ~1.5–2× raw payload size after overhead and fragmentation.
  • Pair with PostgreSQL/MySQL: Redis as cache or ephemeral store, SQL as source of truth.

Remember this

In-memory is Redis's largest speed gain — and its main capacity constraint. Size the working set deliberately.

2. Single-Threaded Command Execution

Redis executes commands single-threaded in the main event loop. One command runs to completion before the next starts — no mutexes on shared data structures, no lock contention between reader and writer threads, no context switching overhead for most operations.

This sounds like a bottleneck until you realize each operation is microseconds. One core chewing through simple commands often beats many cores fighting over locks. Redis 6+ adds I/O threads for reading/writing sockets in parallel, but command execution on the data structures remains single-threaded by default — preserving the no-lock invariant.

The trade-off: one long-running command (KEYS *, huge SMEMBERS, unbounded Lua script) blocks everything behind it. Production teams ban dangerous commands, use SCAN instead of KEYS, and keep scripts short.

Quick reference

  • No lock contention on internal structures — simplicity and predictable latency.
  • I/O threads (optional): parallel network reads/writes; execution still single-threaded.
  • Blocking commands: KEYS, FLUSHALL, heavy SORT — avoid in production.
  • Use SCAN, SSCAN, HSCAN for iteration instead of blocking full-key scans.
  • CPU scaling: run multiple Redis instances (cluster/sharded) rather than one giant thread pool.
  • Contrast: Memcached is also single-threaded; many SQL databases use complex locking.

Remember this

Single-threaded execution trades parallel CPU on one node for zero lock overhead — keep commands fast and non-blocking.

3. Optimized Native Data Structures

Redis is not a dumb string blob store. Each type — string, list, hash, set, sorted set — has a C implementation tuned for its access pattern, and the engine picks internal encodings based on size and content.

Strings use SDS (Simple Dynamic String) — O(1) length, binary-safe, less reallocation than C strings. Lists use quicklist (linked list of ziplists/listpack nodes) for efficient head/tail ops. Hashes and sets start compact (listpack) and upgrade to hash tables when they grow. Sorted sets combine a hash table (member → score) with a skip list for rank/range queries.

You choose the logical type in your app; Redis chooses the physical encoding. That is why HGET on a small hash and ZRANGE on a leaderboard stay fast without you managing indexes manually.

StringsSDSO(1) lengthBinary-safeListsQuickListLPUSH/RPOP O(1)Compact nodesSorted SetsSkip list + hashZRANGE O(log N)Rank queries
Internal encodings behind Redis types

Quick reference

  • SDS: constant-time STRLEN, append-friendly buffer growth.
  • QuickList: doubly linked list of compact nodes — LPUSH/RPOP in O(1).
  • Listpack / hash table dual encoding for hashes and sets — compact when small.
  • Sorted set: skip list + dict — O(log N) rank/range, O(1) score lookup by member.
  • OBJECT ENCODING key — inspect which internal encoding a key uses (debugging).
  • Match command to type: counters on strings, fields on hashes, ranks on sorted sets.

Remember this

Redis speed comes from specialized structures — SDS, quicklist, skip lists — not from storing JSON strings for everything.

4. Mostly O(1) Commands

Core Redis commands run in constant or logarithmic time relative to dataset size. GET and SET on strings, HGET and HSET on hash fields, SADD and SISMEMBER on sets, INCR on counters — all O(1) average case.

Sorted set range queries are O(log N + M) where M is the result size — still predictable for leaderboards and time-series windows. The API surface nudges you toward fast operations; the slow ones (KEYS, unbounded SUNION on huge sets) are documented footguns.

This matters at scale: a cache with 10 million keys still returns a single GET in roughly the same time as a cache with 1,000 keys — unlike scanning a table without an index.

Quick reference

  • O(1): GET, SET, INCR, HGET, HSET, LPUSH, LPOP, SADD, SISMEMBER, SCARD.
  • O(log N): ZADD, ZRANGE, ZRANK — skip list depth grows slowly.
  • Avoid O(N): KEYS, FLUSHDB without understanding, SMEMBERS on massive sets.
  • Pipelining: batch many commands in one round trip without waiting per command.
  • MGET/MSET: amortize network latency across multiple keys.
  • Design keys so hot paths use O(1) ops — not full scans.

Remember this

Redis commands are designed for constant-time hot paths — choose the right type and avoid full-key scans.

5. Event-Driven I/O Multiplexing

Redis uses an event-driven architecture built on efficient I/O multiplexing — epoll on Linux, kqueue on BSD/macOS, IOCP on Windows. One thread watches thousands of client sockets; it wakes only when data is ready to read or write.

No thread-per-connection model (which collapses under 10k clients). The reactor loop: accept connections, read available bytes, parse the Redis protocol (RESP), queue commands, execute, buffer replies, flush when the socket is writable.

That is how Redis sustains hundreds of thousands of concurrent connections on modest hardware — network wait time overlaps with processing other clients' commands.

Event loopepoll / kqueueClientClientClientClientClientClientMultiplex thousands of sockets — no thread per connection
Event loop: one thread, many connections via epoll/kqueue

Quick reference

  • Multiplexing APIs: epoll (Linux), kqueue (macOS/BSD), select/poll fallbacks.
  • RESP protocol: simple text/binary framing — cheap to parse.
  • Connection pooling on client side still recommended — reduces handshake overhead.
  • TLS adds CPU cost — terminate at proxy or use stunnel if needed.
  • Pub/sub and blocking list ops: same event loop — long blocking clients reduce throughput.
  • Contrast with thread-per-request servers at high connection counts.

Remember this

The event loop plus epoll/kqueue lets one process serve many idle connections without thread explosion.

6. Memory-Efficient Encodings

Redis does not allocate a full hash table for a hash with two fields. Small collections use compact encodings (listpack, intset for integer-only sets) and upgrade in place as data grows — similar to dynamic array resizing in application code.

Integers stored as strings may be encoded as raw int64 when parsable. Short strings embed metadata efficiently via SDS. The result: lower RAM per key, higher effective cache density, fewer evictions under maxmemory pressure.

When you benchmark Redis vs "we cached JSON in another store," part of the gap is bytes per key — not just microseconds per op.

Quick reference

  • intset: ordered integer arrays for sets when all members are numbers and count is small.
  • listpack: contiguous encoding for small hashes, lists, and zset entries.
  • Encoding upgrades are automatic — monitor memory when keys grow past thresholds.
  • MEMORY USAGE key — inspect per-key footprint in Redis 4+.
  • Prefer hashes over many string keys when storing object fields — often more compact.
  • Configure maxmemory-policy (allkeys-lru common for pure cache workloads).

Remember this

Compact encodings for small data mean more keys fit in RAM — speed and density compound.

Fast by Design — Not One Trick

Redis is not fast because of one optimization. It is fast because every layer removes work: RAM avoids disk, single-thread avoids locks, native types avoid parsing, O(1) commands avoid scans, multiplexing avoids thread storms, compact encodings avoid wasted RAM.

Remove any layer and you get a different product — durable but slower (disk DB), parallel but contentious (locked structures), generic but verbose (JSON strings only). Redis chose speed and simplicity for in-memory data structure serving — and accepts the trade-offs that come with that scope.

Simple to use. Hard to beat for cache, session store, rate limiter, pub/sub, and leaderboard patterns — as long as you respect memory limits and command complexity.

RAMSingle threadNative typesO(1) opsEvent I/OCompactNot one trick — stacked design decisions
Every layer eliminates unnecessary work

Quick reference

  • Not a replacement for PostgreSQL as primary transactional store.
  • Cluster/sharding for horizontal scale when one node's RAM or CPU caps out.
  • Monitor: latency, memory usage, evicted keys, blocked clients, slowlog.
  • Related: cache-aside patterns in production — see caching strategies article.
  • Alternatives: Memcached (simpler, strings only), KeyDB (multi-thread fork), Dragonfly (modern reimplementation).
  • Use Redis where its six pillars match your access pattern — not everywhere.

Remember this

Redis wins on stacked design decisions — in-memory, single-thread, native types, O(1) ops, event I/O, compact encodings — not a single hack.

Key takeaway

Share:

When someone asks why Redis is so fast, the honest answer is architectural: data stays in RAM, one thread executes commands without lock fights, each type uses a purpose-built C structure, hot commands are O(1), the event loop handles masses of connections cheaply, and small values stay compact until they need to grow.

Use that mental model when sizing clusters, picking commands, and pairing Redis with a durable database. Fast by design — but only for the jobs it was built for.

Related Articles

Caching is the fastest way to scale a system without changing your database or adding servers. Done right, it cuts datab

Read

A query that takes 4 seconds without an index takes 0.2ms with one. That's a 20,000x improvement from a single line of S

Read

Scaling is how a system handles more users, data, or traffic. Vertical scaling (scale up) means giving your existing ser

Read

Keep learning

Follow a structured path or browse all courses to go deeper.