Key-Value Stores
Encode everything you query on into a namespaced key, guard against hot keys, TTL cache data, and never treat a non-persistent cache as a source of truth.
The simplest database, and the discipline it forces
A key-value store is the simplest possible database: a distributed hash map. You GET(key), you
PUT(key, value), you DELETE(key). There is no query language over the value, no WHERE
clause, no join. Because the access path is a hash lookup, point reads and writes are O(1) and the
fastest thing in your architecture: Redis serves reads in tens of microseconds in-process and
sub-millisecond p99 over the network, and a single node handles 100k+ ops/sec easily. This is why KV
stores are the default for caches, session stores, feature flags, rate-limit counters, and as a
building block inside bigger systems.
The defining constraint is value opacity. To the store, the value is a blob of bytes. You cannot
ask "give me all sessions where lastActive < X" because the store cannot see inside the value.
Whatever you want to query on must be encoded into the key. This forces the key-design discipline
that is the whole skill of this family.
Key design
Namespace with a prefix and a delimiter so different data types never collide:
session:{sessionId}, user:123:profile, ratelimit:{userId}:{minuteBucket}. Composite keys
co-locate related lookups. The danger is hot keys: a single key that takes a wildly
disproportionate share of traffic (a global counter, a celebrity's profile) becomes a hotspot on
whichever shard owns it. You fight this by sharding the key (counter:{shard} summed across N
shards) or by fronting the hot key with a client-side or local cache.
Interview nuance: Interviewers probe "cache or source of truth?" Memcached and a Redis instance with no persistence are caches: if the box dies, the data is gone, and that is fine because you can recompute it from the real database. If you use a KV store as a durable source of truth (DynamoDB, or Redis with AOF/RDB persistence and replication), you must reason about durability, replication, and backups just like any primary database. The common wrong turn is treating a cache-configured Redis as a system of record, then losing data on a restart.
TTL, eviction, and the rich data structures
Every session and counter should carry an expiry (SET key val EX 3600) so stale data
self-cleans. When memory fills, Redis evicts by policy: allkeys-lru for a pure cache,
volatile-lru to only evict keys that have a TTL, noeviction to fail writes instead of
dropping data (what you want for a source of truth).
Redis is more than KV. It ships data structures that make it a Swiss-army server: sorted sets (leaderboards, sliding-window rate limits, priority queues), lists (simple queues), hashes (store a session as fields you can update individually), streams (append-only log with consumer groups), pub/sub, HyperLogLog (cardinality estimation), and vector similarity. Reaching for these instead of raw string blobs is often the difference between a clean design and a clumsy one.
Choose the engine to the job: Memcached for a dumb, multi-threaded, memory-only cache; Redis for a single-threaded rich-data-structure store that can also persist; DynamoDB for a managed, durable, auto-sharded KV/document store with predictable single-digit-ms latency at any scale.
Recap: KV stores give O(1) opaque-blob lookups, so encode everything you query on into a namespaced key, guard against hot keys, always TTL cache data, and never treat a non-persistent cache as your source of truth.
You are about to design a session store and rate limiter on Redis. Sort each piece of data by what losing it on a restart would mean.
Apply
Your turn
The task this lesson builds to.
Design the data layout for user sessions and rate-limit counters in a key-value store, including key schema and TTLs.
Think about
- How do you design keys and namespaces to avoid hot keys?
- When is a KV store a cache vs a source of truth?
- What does value-blob opacity mean for your model?
Practice
Make it stick
A second problem on the same idea, so it survives past today.
Design the key-value layer for Twitch's live-stream viewer-count and chat rate-limiting during a top event peaking at 5 million concurrent viewers on a single channel, where a naive global counter would melt one shard. Explain the key schema, the hot-key mitigation, and the consistency you accept.
Think about
- What makes a single channel's viewer count the archetypal hot key?
- Does the displayed count actually need to be exact, and what does relaxing that buy?
- Where can rate-limit enforcement move so a channel-wide limit is not a global lock?