Redis data structures and patterns
The Redis building blocks that appear in system designs: strings and counters, hashes, lists, sets, sorted sets for leaderboards and delayed queues, HyperLogLog, geospatial indexes, streams and pub/sub, Lua scripts, persistence, clustering and pitfalls.
Reading is half of it. See this used in a real interview: walk through Design a Real-Time Leaderboard →
Redis appears in almost every system design diagram, usually as "cache". It is far more useful than that: an in-memory server of data structures that solves leaderboards, rate limiters, session stores, delayed queues, presence, geospatial lookups and counters with a few commands. Knowing which structure fits which problem, and where Redis is the wrong tool, makes those boxes in your design specific and credible.
The structures and what they solve
| Structure | Key commands | Typical uses |
|---|---|---|
| String | GET, SET with EX, INCR, SETNX | cache entries, counters, simple locks, feature values |
| Hash | HSET, HGET, HINCRBY | objects with fields: sessions, user profiles, per-item stats |
| List | LPUSH, RPOP, LRANGE, LTRIM | recent items (capped timelines), simple queues |
| Set | SADD, SISMEMBER, SINTER | unique members, tags, "who liked this", mutual friends |
| Sorted set | ZADD, ZINCRBY, ZRANGE, ZRANGEBYSCORE, ZRANK | leaderboards, priority and delayed queues, sliding-window logs, time-ordered feeds |
| HyperLogLog | PFADD, PFCOUNT, PFMERGE | approximate unique counts in 12 KB |
| Bitmap | SETBIT, BITCOUNT | daily active flags per user id, feature membership |
| Geo | GEOADD, GEOSEARCH | nearby drivers or places (built on sorted sets with geohashes) |
| Stream | XADD, XREADGROUP, XACK | lightweight durable-ish event logs with consumer groups |
| Pub/sub | PUBLISH, SUBSCRIBE | fire-and-forget fan-out to connected servers |
Patterns
Cache-aside with TTL: GET; on a miss, read the database and SET key value EX 300. Add jitter to TTLs and request coalescing for hot keys. See caching.
Leaderboard: ZINCRBY board score user; top 10 with ZRANGE board 0 9 REV WITHSCORES; a user's rank with ZREVRANK. Logarithmic updates and ranks for millions of members. Shard by board or by score range for huge boards. See Design a Leaderboard.
Rate limiter: fixed window with INCR plus EXPIRE; sliding window log with a sorted set of timestamps (ZADD, ZREMRANGEBYSCORE, ZCARD); token bucket in a Lua script for atomicity. See Design a Rate Limiter.
Delayed queue: ZADD jobs due_time job_id; workers fetch due ones with ZRANGEBYSCORE jobs 0 now and claim them atomically. See delayed jobs and distributed cron.
Capped timeline: LPUSH timeline:user post_id then LTRIM timeline:user 0 799. See fan-out on write vs read.
Nearby search: GEOADD drivers lon lat driver_id, GEOSEARCH drivers FROMLONLAT lon lat BYRADIUS 2 km. Frequent location updates are cheap. See Design Uber.
Presence: SET online:user 1 EX 60, refreshed by heartbeats. See presence and connection management.
Distributed lock: SET lock value NX PX 10000 and release with a value-checking script; fine for efficiency, not for correctness. See distributed locks and leases.
Atomicity
Each command is atomic, because Redis executes commands on a single thread. For multi-step logic (check then update), use a Lua script or a function so it runs atomically, or MULTI/EXEC transactions with WATCH for optimistic checks. Keep scripts short: while one runs, everything else waits.
Durability
Redis is in memory first:
- RDB snapshots periodically: compact, but lose writes since the last snapshot.
- AOF (append-only file) logging every write, fsynced every second or every write: less loss, more I/O.
- Replication is asynchronous: a failover can lose recent acknowledged writes.
So treat Redis as a cache or as a store where losing a second of data is acceptable, unless you use a variant designed for stronger durability. Keep the source of truth for money and orders in a database.
Scaling
- Redis Cluster splits keys into 16,384 hash slots across primaries, each with replicas. Multi-key commands work only when keys share a slot; use hash tags (
{user:42}:cart,{user:42}:profile) to co-locate related keys. - Read replicas for read-heavy data (with replication lag).
- Memory is the limit: estimate key count times size, plus overhead. See back-of-envelope estimation.
Pitfalls
- Big keys: a sorted set with 50 million members or a 100 MB string blocks the server during operations and slows migration. Split them.
- Hot keys: one key on one shard takes all the traffic. Replicate or cache locally. See hot keys and skew.
- Slow commands:
KEYS *, largeSMEMBERSorLRANGE 0 -1block everyone; useSCANand bounded ranges. - Eviction surprises: with a memory limit and an eviction policy, keys disappear; make sure that is acceptable for every key type stored there.
- Pub/sub is not durable: disconnected subscribers miss messages. Use streams or a real broker when delivery matters.
Checklist
- Pick the structure that matches the access pattern.
- Lua scripts for multi-step atomic logic.
- Persistence and failover behaviour understood; no money as the only copy.
- Cluster hash tags for related keys; memory estimated.
- No big keys, slow commands or unbounded ranges; hot keys mitigated.