Caching & Databases · lesson 5 of 8 · about 30 min
Redis
The in-memory data server behind sessions, leaderboards, rate limiters and half the caches on the internet. Type real commands, then break it on purpose: a slow command, a crash, a failover, a full memory and a hot key. Tap any for a plain-language card.
Because the data sits in memory (see the memory lessons: RAM is ~1,000 times faster than an SSD), Redis is used as a , a session store, a counter, a leaderboard and a lightweight queue. It trades capacity and durability for speed.
Below is a tiny Redis running in your browser. Type commands or tap the chips. Notice SET, GET, INCR (an atomic counter) and EXPIRE (the key deletes itself after N seconds). Use the ⏱ buttons to move the clock forward.
Run SET greeting hello with EX, then press +10 s a few times and GET it again. A cache that cleans itself.
Try: SET greeting hello EX 20 · then GET greeting · then press +10 s
Keyspace (0)empty
Successful commands: 0. Real Redis has hundreds of commands; this one speaks the core ones.
Data structures
Pick the structure that already does the job.
String: a value, a counter, a cached page.
Hash: an object with fields (a user session).
List: push and pop at both ends (a simple queue).
Set: unique members, fast membership test.
Sorted set: members ordered by a score (leaderboards, delayed jobs).
Also: streams (append-only log), bitmaps, HyperLogLog (approximate unique counts), geo indexes, and more.
Complete at least four of the five challenges in the terminal. Each one mirrors a real use of Redis.
Every command here is atomic: two clients incrementing the same counter can never lose an update. That is why Redis suits counters and rate limits.
Rate counterINCR rl:user1 three times, then EXPIRE rl:user1 60
○
Unique visitorsSADD visitors asha, SADD visitors ravi, then SADD visitors asha again (it returns 0: no duplicates)
○
Job queueLPUSH jobs a, LPUSH jobs b, LPUSH jobs c, then RPOP jobs (first in, first out)
○
User sessionHSET session:42 user asha role admin, then EXPIRE session:42 1800
Work through the challenges above, one command at a time.
Keyspace (0)empty
0 of 5 challenges done.
Why it is fast, and its trap
One thread. Fast, until one command is slow.
The trap: a command that takes 800 ms stops everyone. Every request behind it waits in line. KEYS * on a big database is the classic example. in a one-at-a-time system becomes everyone's latency. The safe version is SCAN: it does the same work in many tiny batches so other requests slip in between.
Below, 200 normal requests arrive over 2 seconds (one every 10 ms). At 600 ms someone runs the big command. Compare the three runs.
Same work, same server. With SCAN the heavy job is split into small pieces and nobody notices.
Typical (p50)0.2 ms
Slowest 1% (p99)0 ms
Requests slowed >20 ms0
Every request finishes in a fraction of a millisecond. This is Redis at its best.
Surviving a restart
RAM forgets. How much are you willing to lose?
The official guidance: use both if you want data safety comparable to a normal database; use RDB alone if a few minutes of loss is fine; use no persistence when Redis is only a cache that can be refilled.
Writes arrive at 10 per second. Drag the crash time and see how many writes each policy loses.
It is the same trade-off as the fsync scene: durability costs waiting. “Always” waits on the disk for every write batch.
⚡
0 s90 s180 s
Writes before the crash950
Lost in the crash350
Safe on disk600
The last snapshot was at 60 s, so the 350 writes since then are lost. Snapshots are compact and restart fast, but expect to lose minutes.
save 60 … · cheap: a child process writes a point-in-time file
Copies and failover
A replica is a copy that is always slightly behind.
If the primary dies in that gap, a replica is promoted (by Sentinel or by Redis Cluster) and the writes it never received are gone, even though the client was told they succeeded. Redis's own docs say it plainly: it does not guarantee strong consistency.
The WAIT numreplicas timeout command blocks until that many replicas have acknowledged. It narrows the window for the writes you wait on, but it is not a guarantee by itself: a failover can still pick a replica that missed a write. Try it below.
Run the traffic for a few seconds, then crash the primary. Do it once with WAIT off and once with WAIT on.
Primary
0
writes applied
→ 0 in flight →
Replica
0
writes received
Client was told OK0
On the replica0
OK'd but lost–
Send some writes, then crash the primary. Watch the gap between the two bars: those are writes the replica has not seen yet.
Spreading data over many servers
16,384 slots decide which server owns a key.
That is why adding a server is cheap: hand over some slots and only the keys in them move. The naive way (hash mod number-of-servers) reshuffles most keys when the count changes.
The catch: a command touching several keys works only if they are all in the same slot. Otherwise you get a CROSSSLOT error. The fix is a hash tag: only the part inside {...} is hashed, so {user:1}:cart and {user:1}:profile always land together.
Type your own keys. Then add the fourth server and compare how many keys had to move.
user:1000slot 1649CRC16 18033 mod 16384→ server A
cart:1000slot 7883CRC16 24267 mod 16384→ server B
Different slots (1649 and 7883): MGET user:1000 cart:1000 fails with (error) CROSSSLOT Keys in request don't hash to the same slot.
Server A665 of 2,000 keys
Server B677 of 2,000 keys
Server C658 of 2,000 keys
Measured on 2,000 keys named user:1 … user:2000 using the same CRC16 Redis Cluster uses. In production, redis-cli --cluster reshard moves slots live, key by key.
When memory runs out
RAM is finite. Decide what Redis forgets.
Separately, keys with a expire in two ways: lazily, when someone touches an expired key, and actively, where Redis keeps testing random keys that have a TTL and deletes the expired ones, repeating while more than about a quarter of the sample was expired.
Below, the same 6,000 requests (a few keys are very popular) are replayed against every policy. Shrink the memory, or add a one-time scan of 1,000 new keys, and watch who copes.
For a pure cache use an allkeys-* policy. volatile-* and noeviction are for Redis holding data you cannot afford to lose.
noeviction56%2,553 writes refused
allkeys-random56%Evicts a random key. Cheap and surprisingly not terrible.
allkeys-lru59%Evicts the least recently used key (approximated by sampling 5 keys, like real Redis).
allkeys-lfu65%Evicts the least frequently used key (also sampled). Resists one-time scans.
volatile-lru33%3,863 writes refused
On a few-hot-keys workload allkeys-lfu leads at 65%. noeviction stops caching new keys the moment memory fills, so its hit ratio freezes.
Hit ratio = share of the 6,000 requests answered from Redis. Each miss tries to SET the key (cache-aside). Half the keys have a TTL, which matters for volatile-lru.
Should it be in Redis?
Great fit, fine with care, or wrong tool?
Eight real jobs. Knowing what not to put in Redis is half of using it well. Each answer is worth 20 XP.
Ask two questions: can I rebuild this if Redis loses it? and does it fit in RAM at a sane price?
Question 1 / 8
Rendered product pages in front of a SQL database, fine to be 5 minutes old.
Tip: press 1 to 3 to answer, Enter for the next one.
Who lives by this
Redis is the quiet workhorse.
Four real examples of what teams put in memory, and why.
Timelines in RAMTwitter
tweet → fan-out into each follower's timeline list in Redis → read on app open
Twitter engineers have described keeping each user's home timeline as a list in Redis, so opening the app reads a ready-made list instead of assembling one from scratch. Writes do more work so that reads are almost free.
Memory tuningInstagram
media id → user id, ~300 million pairs stored as Redis hashes
Instagram's engineering blog described mapping 300 million media ids to user ids. Storing each pair as its own key was wasteful, so they bucketed many pairs into small hashes, which Redis stores compactly, and cut memory use from roughly 21 GB to about 5 GB.
The shared cacheStack Overflow
web servers → Redis (shared cache) → SQL Server
Stack Overflow's published architecture notes show a small fleet of web servers sharing Redis as the cache layer in front of SQL Server. A tiny number of well-fed machines, with Redis absorbing the repeated reads.
Managed and durableAWS ElastiCache and MemoryDB
app → managed Redis-compatible nodes, replicated across availability zones
Cloud providers run Redis-compatible services so you do not run failover yourself. ElastiCache is the cache flavour; MemoryDB adds a multi-zone transaction log so acknowledged writes survive a failover, which is the durability plain async replication lacks.
Interview angle
What interviewers actually ask.
Six questions, each worth 20 XP. Redis questions are really about trade-offs: speed against durability, and single node against cluster.
Whenever you say “I would use Redis”, add one sentence: what happens when it restarts, fails over, or fills up.
Question 1 / 6
Someone runs KEYS * on a database with millions of keys in production. What happens to other clients?
Tip: press 1 to 3 to answer, Enter for the next one.
Break it
One celebrity key melts one server.
Raise the hot key's share of traffic until its shard turns red. Then try the three standard fixes: a local cache in each app server, splitting the key into copies on different shards (post:7:1 … post:7:4, pick one at random), and read replicas of the hot shard.
Capacity of one node here is 100,000 ops/s (illustrative). Total capacity is 4 × that, but a hot key can only use one node's worth.
Shard 1 🔥
110k
Shard 2
30k
Shard 3
30k
Shard 4
30k
Busiest node110%
Traffic actually served95%
The hot shard is asked for 110k ops/s but can do 100k. It queues, latency climbs for every key on it, timeouts begin, and retries make it worse. The other shards are mostly idle.