Most people meet Redis as "a cache," but that undersells it. Redis is an in-memory data structure store: a server that holds rich data types — strings, hashes, lists, sets, sorted sets, streams — in RAM and exposes atomic operations on them over the network. That combination of data structures plus microsecond latency is why Redis ends up behind caching, session stores, rate limiters, leaderboards, queues, and pub/sub, often all in the same system. This is the technology our caching and leaderboard articles keep reaching for; here's how it actually works.
- Not just a cache — a data structure server. Strings, hashes, lists, sets, sorted sets, streams, bitmaps, HyperLogLog, and geo, each with atomic O(1)/O(log n) operations.
- In-memory + single-threaded for command execution — RAM speed plus no locks means simple, predictable, extremely fast operations (~100K+ ops/sec per node).
- Persistence is optional and tunable — RDB snapshots (compact, fast restart, can lose recent writes) vs AOF append-only log (more durable, larger); often both.
- Eviction makes it a cache — set
maxmemoryand a policy (LRU/LFU/TTL) and Redis evicts under pressure. - HA via replication + Sentinel; scale via Cluster — Cluster shards the keyspace across 16,384 hash slots.
- Watch big keys, hot keys, and blocking commands — one slow O(n) command stalls the single thread for everyone.
Redis keeps typed data structures in memory and runs commands on a single thread, so operations are atomic and blazing fast. Persist with RDB (snapshots) and/or AOF (write log) depending on how much data loss you can tolerate. Use eviction policies to run it as a cache, replication + Sentinel for high availability, and Cluster (hash slots) to scale horizontally. Its data types unlock far more than caching: leaderboards (sorted sets), rate limiting, queues, pub/sub, and locks.
Why In-Memory and Single-Threaded
Two design choices explain Redis's speed. First, it keeps data in RAM, so reads and writes avoid disk entirely — latencies are microseconds, not milliseconds. Second, command execution is single-threaded: one command runs at a time on one core, processed through an event loop. That sounds like a limitation, but it's a feature: there are no locks, no race conditions between commands, and every operation is naturally atomic. You never get a half-applied increment. The CPU is rarely the bottleneck for an in-memory store — the network and memory bandwidth are — so a single thread comfortably handles 100K+ operations per second. (Modern Redis does use threads for I/O and background tasks, but the core command execution remains single-threaded.)
Because one thread serves everyone, a single slow command blocks all other clients. Running KEYS * or a big SMEMBERS on a million-element set freezes the server for the duration. The cardinal rule of operating Redis is: keep individual commands fast and avoid O(n) operations on large structures on the hot path.
The Data Structures
What sets Redis apart from a plain key-value cache like Memcached is its typed values with operations that run server-side:
- Strings — bytes, integers (atomic
INCR), or blobs. The basic cache value. - Hashes — field→value maps; store an object without serializing the whole thing.
- Lists — linked lists;
LPUSH/RPOPmake simple queues and stacks. - Sets — unordered unique members; membership, union/intersection (e.g. mutual friends).
- Sorted sets (ZSET) — members ordered by score; the engine behind leaderboards and rate limiters.
- Streams — append-only log with consumer groups; a lightweight Kafka-like primitive.
- Bitmaps / HyperLogLog / Geo — space-efficient counters, approximate cardinality, and geospatial queries.
SET session:42 "..." EX 3600 # string + 1h TTL
INCR views:home # atomic counter
HSET user:42 name "Ada" tier gold # object as a hash
ZADD board 9820 alice # sorted set → leaderboard
ZREVRANK board alice # O(log n) rank
LPUSH jobs "task1" / BRPOP jobs 0 # simple queue
Persistence: RDB vs AOF
In-memory doesn't have to mean ephemeral. Redis offers two persistence mechanisms so it can survive restarts:
| Aspect | RDB (snapshot) | AOF (append-only file) |
|---|---|---|
| What it stores | Point-in-time dump of the dataset | Every write command, replayed on start |
| Durability | Can lose writes since last snapshot | Up to ~1s loss (fsync everysec) |
| File size / restart | Compact; fast restart | Larger; slower replay |
| Cost | Periodic fork can spike memory | Continuous write + rewrite/compaction |
RDB is great for backups and fast restarts; AOF is better when you can't afford to lose more than a second of writes. The common production setup runs both — AOF for durability, RDB for quick backups — and many use a hybrid format. If you're using Redis purely as a cache, you might disable persistence entirely and treat data as recomputable.
Eviction and Expiration
To use Redis as a cache, you cap its memory with maxmemory and pick an eviction policy for when it's full: allkeys-lru (evict least-recently-used), allkeys-lfu (least-frequently-used — better for skewed access), volatile-ttl (evict soonest-to-expire among keys with a TTL), or noeviction (reject writes). Separately, keys can carry a TTL via EXPIRE. Redis expires keys both lazily (on access) and actively (a background sampler removes expired keys), balancing CPU against memory. Eviction (note: Redis approximates LRU/LFU by sampling, not a perfect global ordering) is what lets it serve as the cache layer described in our caching deep dive.
Replication and High Availability
A single Redis node is a single point of failure. Replication adds one or more replicas that asynchronously copy the primary's data — useful for read scaling and as warm standbys. For automatic failover, Redis Sentinel monitors the primary and replicas; if the primary goes down, Sentinel reaches a quorum, promotes a replica, and reconfigures clients. Because replication is asynchronous, a failover can lose the last few writes that hadn't reached the promoted replica — the same durability-vs-availability tradeoff seen across distributed systems (see our DDIA notes on replication).
Redis Cluster: Horizontal Scaling
When the dataset or write throughput outgrows one node, Redis Cluster shards the keyspace across multiple primaries. It divides the key space into 16,384 hash slots; each key maps to a slot via CRC16(key) mod 16384, and each primary owns a range of slots. Adding a node means moving some slots to it — only the affected keys migrate, not the whole keyspace.
slot(key) = CRC16(key) mod 16384
node A: slots 0 – 5460 (+ replica)
node B: slots 5461 – 10922 (+ replica)
node C: slots 10923 – 16383 (+ replica)
multi-key ops must share a slot → use a {hashtag}:
MGET {cart:42}:items {cart:42}:total # both hash on "cart:42"
The catch: multi-key operations (and transactions) only work if all keys live in the same slot, which you force with a hash tag — the part in {} is what's hashed. This is the main thing that makes app code Cluster-aware.
What Redis Is Actually Used For
The data structures map directly onto a surprising range of jobs:
- Cache — the classic: cache-aside in front of a database.
- Session store — fast, TTL'd user sessions shared across stateless app servers.
- Rate limiter — atomic
INCRwith anEXPIRE, or a sorted set for sliding windows. - Leaderboard — sorted sets give O(log n) rank and top-k (see leaderboard design).
- Queues — lists (
LPUSH/BRPOP) or streams with consumer groups. - Pub/Sub — lightweight fan-out messaging for real-time features.
- Distributed lock —
SET key val NX EXas a lease; the Redlock algorithm extends this across nodes (though it's debated — single-instance locks with fencing are often enough).
Redis vs Memcached
| Aspect | Redis | Memcached |
|---|---|---|
| Data types | Rich (lists, sets, ZSETs, streams…) | Strings/blobs only |
| Persistence | RDB + AOF | None (pure cache) |
| Replication / HA | Yes (replicas, Sentinel, Cluster) | No built-in replication |
| Threading | Single-threaded core | Multi-threaded |
| Best for | Versatile data structures, durability | Simple, large multi-core key-value cache |
Pitfalls
- Big keys — a single huge value (a million-element list) makes every operation on it slow and stalls the thread; split or restructure.
- Hot keys — one key getting disproportionate traffic concentrates load on its node; client-side caching or key-splitting helps.
- Blocking commands — avoid
KEYS, largeSMEMBERS/HGETALLin production; useSCANfor iteration. - Memory & persistence interaction — RDB's fork can transiently double memory; size headroom accordingly.
Redis is best understood as a single-threaded, in-memory data-structure server — that framing explains its speed (RAM + no locks + atomic ops), its main risk (one slow command blocks everyone), and its versatility (the right data type turns Redis into a cache, queue, leaderboard, or lock). Reach for RDB/AOF when you need durability, Sentinel for failover, and Cluster (hash slots) when one node isn't enough.
Why is single-threaded Redis fast? Data is in RAM and one command runs at a time, so operations are atomic and lock-free; network/memory, not CPU, is the bottleneck.
RDB vs AOF? RDB = compact point-in-time snapshots, fast restart, can lose recent writes; AOF = append every write, ~1s durability, larger and slower to replay. Often run both.
How does Redis Cluster shard? 16,384 hash slots; key→slot via CRC16 mod 16384; each primary owns a slot range. Multi-key ops need a shared slot via a {hash tag}.
Redis vs Memcached? Redis has rich data types, persistence, and replication; Memcached is a simpler multi-threaded blob cache. Pick Redis unless you only need a plain cache.
Biggest operational risk? A slow O(n) command (KEYS, big collection reads) blocking the single thread for all clients.