Module 07 · Databases · Lesson 8 of 13
Key-value stores
The simplest data model and the fastest: a value behind a key. Why that simplicity scales so well, how key design replaces query design, in-memory versus durable stores, and the patterns that cover most uses.
Why it matters
Every scalable system has a layer that just needs to answer "give me the thing for this id" a million times a second. Sessions, feature flags, rate-limit counters, user profiles, shopping carts, cached pages. A key-value store does exactly that and nothing else, and because it does nothing else it partitions perfectly, replicates simply and serves lookups in well under a millisecond. The skill is designing keys so that everything you need is a lookup.
The idea
A key-value store maps a unique key (usually a string or bytes) to an opaque value. The API is essentially get(key), put(key, value), delete(key), often with a time-to-live. There are no joins, no secondary queries, no schema. Data is spread across nodes by hashing the key, so capacity and throughput grow linearly with nodes and every lookup touches exactly one of them.
Key design is the whole design
Since you can only fetch by key, the key must encode the question. Conventions: a namespace prefix, the entity, and the id, such as user:42:profile or cart:session-7f3a. Composite keys answer composite questions: ratelimit:api-key-9:2026-11-08T14:07 holds this minute's counter. Lists and sets inside values (Redis hashes, lists, sorted sets; DynamoDB maps and lists) let one key hold a small collection. Anything that needs "find all users where..." is not a key-value question; either maintain an index key yourself (users-by-city:paris holding ids) or use a different store.
In-memory versus durable
| In-memory (Redis, Memcached) | Durable (DynamoDB, Riak, RocksDB-based) | |
|---|---|---|
| Latency | Sub-millisecond | Single-digit milliseconds |
| Durability | Optional snapshots or append-only log; treat as rebuildable unless configured otherwise | Persisted and replicated; a system of record |
| Capacity | Bounded by RAM; cost per gigabyte is high | Disk-sized; cheap per gigabyte |
| Data structures | Rich (hashes, sorted sets, streams, pub/sub) | Simpler values, sometimes with maps and lists |
| Typical use | Caches, sessions, counters, leaderboards, locks, queues | Profiles, carts, device state, anything that must survive and scale |
Patterns that cover most uses
Sessions and carts keyed by session id with a TTL. Rate limiting with atomic increments on a per-window key. Caching with cache-aside and TTLs, covered in the Caching module. Leaderboards with sorted sets. Distributed locks with set-if-not-exists plus expiry. Idempotency keys with a TTL of a day. Feature flags and configuration read on every request. Each of these is one key, one operation, and no coordination between nodes.
Consistency and scaling
Because each key lives on one partition, single-key operations can be atomic and strongly consistent cheaply; multi-key operations across partitions cannot, which is why "transactions" in key-value stores are limited or expensive. Scaling is horizontal by adding partitions; the risk is a hot key that concentrates traffic on one node, handled by splitting or caching locally. DynamoDB-style stores add a sort key within a partition, which turns the model into "find by partition, range within it" and covers many one-to-many patterns, the bridge to wide-column stores in the next lessons.
Trade-offs
Key-value stores buy speed, linear scaling and operational simplicity, and pay with the absence of queries: every access path must be designed up front as a key, and changing access patterns later means rewriting data. Use them for lookups you can name in advance and for the hot, simple parts of a system, not as the only store for data you will need to ask new questions of.
In an interview
Be concrete about keys: "Sessions in Redis keyed by session id with a 24-hour TTL, roughly one kilobyte each, so a million active sessions is a gigabyte. Rate limits are atomic increments on a per-key-per-minute counter. Both are rebuildable, so Redis runs with replicas but no strict persistence." That mix of key design, sizing and durability stance is what the question is testing.
Check yourself
A product team wants to add 'list all users in a given city' to a system whose user data lives only in a key-value store keyed by user id. What is the honest answer?