Redis for System Design Problems

Dated Sep 3, 2026; last modified on Fri, 04 Sep 2026

Redis Basics

Redis is a key-value store that lives in memory, and executes one command at a time. In-memory nature makes it very fast (a single node can handle 100K writes per second), while the sequential model makes it easy to reason about operations.

Redis has two persistence modes. RDB takes periodic snapshots, and AOF logs every write but only fsyncs once per second by default – therefore, Redis is not durable. While you can configure AOF to fsync on every write, you’d lose the speed that you came to Redis for.

The key-value store is defined such that the key is always a string. The value can be any supported data structure, e.g., string, dictionary, list, set, priority queue, append-only log, geospatial index, bloom filter, JSON, time series, etc.

Redis supports commands which act on the value. For example, on an unordered collection of unique strings, Redis has commands such as: SADD to add one/more members to the set, SCARD returns the number of members in the set, SISMEMBER determines whether an element is a member of the set, etc.

Redis Infrastructure Configurations

Common Redis configurations. Credits: HelloInterview.

Common Redis configurations. Credits: HelloInterview.

Redis can run as a single node, or with a high availability replica. Note that Redis replication is asynchronous, i.e., the primary acknowledges your write before the replica has seen it. If the primary dies and the replica is promoted, last moments of acknowledged writes can vanish. For this reason, don’t use Redis as a system of record.

Redis can also be deployed as a cluster. Every key hashes to one of 16,384 hash slots, and each slot is assigned to a node. Nodes share cluster state with each other, and so every node knows the full slot map. If a client asks the wrong node for a key, the clients gets back a MOVED reply pointing at the right node; the request is not forwarded. Clients can refresh its map via CLUSTER SHARDS.

Redis expects all the data for a given request to be on a single node, and so choosing your keys is how you scale Redis. Keys like {user:123}:posts and {user:123}:likes always land in the same slot because only the part of the key inside {braces} gets hashed.

Redis as a Cache

This is the most common deployment scenario. See Caching > Redis as a Cache .

Redis as a Distributed Lock

Redis works here because it’s one shared server that all your app servers can each, and every command executes atomically. See Distributed Systems Potpourri > Distributed Lock .

Redis for Leaderboards

A Redis sorted set is a collection of unique strings ordered by an associated score. For example, to track an ordered list of the highest scores in a massive online game:

ZADD racer_scores 10 "Norem"
ZADD racer_scores 12 "Castilla"
ZADD racer_scores 8 "Sam-Bodden" 10 "Royce" 6 "Ford" 14 "Prickett"

ZRANGE racer_scores 0 -1 returns members in ascending order. ZRANGEBYSCORE racer_scores -inf 10 returns all races with 10 or fewer points. ZADD racer_scores 15 "Norem" changes Norem’s score to 15, while ZINCRBY racer_scores 10 "Norem" increases Norem’s score by 10.

Most sorted set operations are \(\mathcal{O}(log(n))\), where \(n\) is the number of members. ZRANGE is \(\mathcal{O}(log(n) + m)\), where \(m\) is the number of results returned.

Redis for Rate Limiting

Consider a fixed-window rate limiter where we guarantee that the number of requests does not exceed \(N\) over some fixed window of time \(W\). On an incoming request, INCR the counter key for the current window. In the first request, INCR returns 1 and so set the expiry. On subsequent requests, INCR returns some number; when INCR returns a number \(\gt N\), reject the request (a 429 with a Retry-After header). Run INCR and EXPIRE as one Lua script for atomicity.

For a sliding window, keep one sorted set per user with the request timestamp as the score. On each request, ZREMRANGEBYSCORE to remove entries older than the window, ZCARD to count what’s left, and if the counter is under \(N\), ZADD the new request. As before, run the sequence as a Lua script for atomic execution.

Redis for Event Sourcing

Event-sourced designs store an ordered log of events and derive state from it. Producers append items, e.g., XADD to a queue. A worker reads an item via XREADGROUP, processes it, and acknowledges it. The consumer group tracks which items are pending with which worker. Each pending entry carries an idle time – when a worker dies mid-task (or is too slow), the entry’s idle time keeps climbing until another worker XCLAIMs it and restarts the job. Your processing should be idempotent.

That said, Redis’ asynchronous replication means that a crash can lose recent entries. For durable ordered throughput, long retention, and replay for independent consumers, go for Kafka instead.

Redis for Pub/Sub

Redis Pub/Sub natively supports broadcasting messages to multiple subscribers in real time, e.g.,

SUBSCRIBE news.* # Listen for messages sent to news.art, news.science, etc.
PUBLISH news.art https://foo.com/monalisa # A channel is just an agreed upon string

Redis pub/sub configuration. Each publisher sends messages to the
node the channel is assigned. Regardless of the number of channels, each
subscriber makes one connection per node. Credits: HelloInterview.

Redis pub/sub configuration. Each publisher sends messages to the node the channel is assigned. Regardless of the number of channels, each subscriber makes one connection per node. Credits: HelloInterview.

Sharded Pub/Sub, SPUBLISH/SSUBSCRIBE, routes each channel to the shard that owns its slot, allowing capacity to scale with the cluster.

A subscriber holds one connection to a node, and receives all of its subscribed channels over that single connection. That way, there’s no additional overhead for having multiple channels.

Redis Pub/Sub uses at-most-once delivery. Subscribers that are offline when a message if published will miss that message entirely. For message persistence, delivery guarantees, or replaying missed messages, consider Redis Streams, Kafka, or RabbitMQ.

When the last subscriber of a channel disconnects, then that channel is removed from memory. Subsequent publishes to that channel get dropped.

This rhymes with the observer design pattern . If a subject’s observer list is empty, then:

for (let observer of observers)
  observer.Notify();

… is a no-op. However, it’s up to the observers to unsubscribe themselves, the subject does not need to infer that by themselves.

References

  1. Redis Deep Dive for System Design Interviews | Hello Interview System Design in a Hurry. www.hellointerview.com . Jan 16, 2026. Accessed Sep 3, 2026.
  2. Redis 8.10 Commands Reference | Docs. redis.io . Accessed Sep 3, 2026.
  3. Redis sorted sets | Docs. redis.io . Accessed Sep 4, 2026.