Key-Value Stores
Key-value stores are the simplest NoSQL model: a flat dictionary mapping keys to opaque values. Lookups are O(1) hash operations, writes are append-friendly, and the engine makes almost no assumptions about what the value contains. Redis and DynamoDB are the canonical examples, but they sit on opposite ends of the consistency/ durability spectrum — Redis is an in-memory cache, DynamoDB is a durable, replicated, multi-region store.
Foundational.
How it works
A key-value (KV) store is exactly what it sounds like: a giant hash map. You give it a key, it gives you back a value. The store doesn't understand what's inside the value — it could be a string, a JSON blob, a serialized Protobuf, or raw bytes. The database treats it as opaque.
This simplicity is the entire point. Because the engine doesn't need to parse, validate, or index into the value, point reads and writes are extremely fast — typically O(1) hash lookups, often single-digit microseconds when data fits in memory. There are no JOINs, no secondary indexes (in pure KV), and no query planner.
The two reference implementations are very different beasts:
- Redis — an in-memory data-structure server. Sub-millisecond operations, optional persistence via snapshotting or AOF (append-only file). Used as a cache, queue, leaderboard, and distributed lock.
- DynamoDB — AWS's managed, durable, replicated KV/document store. Data is persisted across three facilities, automatically partitioned, and scales to virtually unlimited throughput. Used as a primary database for serverless apps.
Both are key-value stores, but they live at opposite ends of the durability/consistency spectrum. Redis is fast and volatile; DynamoDB is slower and durable. Picking 'a key-value store' without specifying which end of that spectrum you need is the most common mistake.
What makes Redis more than a hash map is its rich set of value types. A key's value can be a string, a list, a set, a sorted set (the backbone of leaderboards and rate limiters), a hash, a stream, or a HyperLogLog. Each type comes with atomic server-side operations — INCR, ZADD, LPUSH, HSET, XADD — so you can build counters, queues, and leaderboards without read-modify-write cycles.
DynamoDB, by contrast, treats values as opaque items (a flat map of attribute name → value). You can do GetItem, PutItem, UpdateItem, and DeleteItem by primary key. To query by something other than the key, you define a Global Secondary Index (GSI) or a Local Secondary Index — DynamoDB manages these as separate materialized tables. There is no ad-hoc query language like SQL.
The mental model:
- Redis: a fast, in-memory data-structure toolkit you reach for to make your hot path faster.
- DynamoDB: a horizontally scalable, durable KV store with tunable consistency that you reach for when your SQL primary can't scale to the write throughput or schema flexibility you need.
DynamoDB replicates each item across three nodes. A read can be Eventually Consistent (default, cheaper, ~50% of cost) or Strongly Consistent (returns the latest write, more expensive, not available if you read from a global table replica). This is a per-request decision, not a database-wide setting — a great example of letting the caller pick the trade-off. Redis, by contrast, is single-threaded and strongly consistent within a single instance; once you cluster Redis, you accept eventual consistency for cross-slot operations.
When to use a key-value store:
- Caching — Redis as a cache in front of your SQL database is the textbook pattern. Lookups by primary key map directly to KV operations.
- Sessions and tokens — short-lived, accessed by ID, with TTL semantics. Redis
EXPIREis a perfect fit. - Counters and rate limiters — atomic
INCR+EXPIREper IP or user gives a sliding-window rate limiter in two round-trips. - Leaderboards and rankings — Redis sorted sets (
ZADD,ZREVRANGE) implement this in a single command. - Distributed locks — Redis with
SET NX PX(or Redlock for multi-node) is a common lock primitive. - Primary storage with simple access patterns — DynamoDB shines when you always access an item by its key (user profile, configuration, shopping cart).
When NOT to use a key-value store:
- Ad-hoc queries — there's no query planner. If you need
WHERE age > 30 AND country = 'US', you need a secondary index or a different model. - Multi-row transactions — pure KV stores don't have ACID transactions across keys (Redis has Lua scripts and MULTI/EXEC for a single shard; DynamoDB has transactions limited to 100 items).
- Range scans — DynamoDB range queries only work on a sort key within a partition; KV stores aren't built for analytical scans.
- Complex relationships — use a relational DB or a graph DB.
The rule of thumb: if your access pattern is 'I have the key, give me the value', a KV store is appropriate. If you find yourself adding more and more secondary indexes to mimic SQL, you've picked the wrong model.
You need to implement a global leaderboard for a mobile game: top 100 players by score, updated thousands of times per second, read on every page load. What's the best data store and why?
Pick one answer.
You're building a serverless e-commerce API on AWS and need to store user shopping carts. Items are always accessed by `user_id`, no JOINs, throughput can spike 100x on Black Friday. Which store fits best, and what's the main reason?
Pick one answer.
What's the single most important difference between Redis and DynamoDB for an architect choosing a key-value store?
Pick one answer.
Engineering mental model
Mental model. Think of Key-Value Stores as a deliberate boundary in a system. The boundary exists because something becomes harder to manage when everything is done in one place: latency, scale, failure isolation, consistency, cost, or team ownership. The useful question is not “what does Key-Value Stores mean?” but “what pressure makes this boundary worth introducing, and what new failure mode does it create?”
Before choosing Key-Value Stores, name the workload, the critical user path, the dominant bottleneck, the failure you are trying to absorb, and the trade-off you are willing to accept. If you cannot name those five things, the design is probably premature.
SELECT id, created_at
FROM records
WHERE tenant_id = ?
ORDER BY created_at DESC
LIMIT 50;
-- Ask: which index makes this query predictable at scale?Back-of-the-envelope reasoning
Numerical lens: write down traffic, payload size, read/write ratio, peak multiplier and durability target before choosing a component. The numbers should justify the architecture.
Interactive thought experiment: Key-Value Stores
Change the variables below and predict what breaks first in Key-Value Stores. The production lab can later reuse these same inputs.
Change one variable at a time. Predict the failure mode first, then move the slider and see whether your mental model matches the simplified system response.
If you are stuck on Key-Value Stores, start by drawing the request path and marking every network hop, stateful component, queue, cache and failure boundary. Then estimate where the system will saturate.
You increase traffic by 10× in a system using Key-Value Stores. What should you inspect first?
Pick one answer.
Which statement is the safest engineering habit when using Key-Value Stores?
Pick one answer.
You have dashboards for traffic, latency, errors and saturation. You can change the architecture, but every change has operational cost.
Production scenario: your system uses Key-Value Stores, traffic suddenly spikes, and p99 latency doubles. What is your first move?
Interview drill
Answer this without notes: When would you choose Key-Value Stores, and when would you intentionally avoid it? Mention at least one bottleneck it addresses, one failure mode it introduces, and one alternative. Then quantify the workload you are designing for.
For Key-Value Stores, start with access patterns rather than brand names. Identify the dominant reads/writes, data relationships, consistency requirements, partition key, hot keys and failure behavior before choosing a storage strategy.
Numerical sanity check
A rough capacity check: required write throughput ≈ peak writes/s × average record size. At 5,000 writes/s and 2 KB average payloads, the raw incoming data stream is about 10 MB/s before indexes, replication and overhead.
Do not optimize for a memorized definition. Reason from the workload and failure mode.
A team proposes Key-Value Stores because it 'scales'. What workload characteristic would make that choice a poor fit?
Pick one answer.
What you gain, what you pay
- +O(1) point lookups — fastest possible reads for the access pattern they optimize.
- +Simple operational model — no query planner, no schema migrations, no JOINs to tune.
- +Horizontal scalability — DynamoDB partitions automatically; Redis Cluster shards by hash slot.
- +Schema flexibility — values are opaque, so schema evolution is an application concern, not a DB migration.
- +Rich server-side operations in Redis (INCR, ZADD, XADD) enable atomic complex updates without read-modify-write.
- −No ad-hoc querying — if you didn't plan for an access pattern, you can't query it efficiently.
- −No JOINs — relationships must be denormalized or handled in application code.
- −Limited transactions — Redis MULTI is single-shard; DynamoDB transactions cap at 100 items.
- −Redis volatility — in-memory means data loss risk; you must choose what to persist and how.
- −DynamoDB cost surprise — sustained high throughput can be dramatically more expensive than self-hosted alternatives.
- −Hot keys — a single popular key (e.g., a viral celebrity's profile) can saturate one partition and become a bottleneck.
How this breaks in production
- Redis OOM eviction — when memory fills, Redis evicts keys by policy (LRU/LFU); if you persist critical data only in Redis, you lose it.
- Hot partition in DynamoDB — a single partition key receiving all writes (e.g., a global counter) hits the 1000 WCU/partition ceiling and throttles.
- Cache stampede — Redis cold start or restart causes thundering herd on the backing store.
- Treating Redis as durable primary storage — losing it on crash reveals what was never supposed to live there.
- Unbounded key growth — never expiring keys (no TTL) cause Redis to grow indefinitely and eventually OOM.
- Cross-slot operations in Redis Cluster — MULTI/EXEC, SUNION, etc. fail if keys hash to different slots; design keys carefully.
Don't fall into these traps
- •Choosing a KV store without writing down the access patterns first — then discovering you need a query the store can't run.
- •Storing the source of truth in Redis — it's a cache. The source of truth should be a durable store.
- •Not setting TTLs on ephemeral data (sessions, rate-limit buckets) — Redis grows until it evicts something you cared about.
- •Treating DynamoDB like SQL — designing for ad-hoc queries instead of one-query-per-access-pattern.
- •Forgetting Redis Cluster's hash-slot constraint when naming keys — wrap related keys with `{tag}` for hash-tag locality.
- •Underestimating DynamoDB costs — on-demand pricing at scale can dwarf self-hosted Postgres; model it before committing.
Real systems using this
How real systems implement this
- Discord — Originally ran on a 5-node Cassandra cluster for messages but moved to ScyllaDB (a C++ wide-column store) for higher throughput. Used Redis for caching and presence.
- Snapchat — DynamoDB stores user profiles, snaps metadata, and stories. They've publicly described auto-scaling and how they handle hot partitions by sharding keys in the application layer.
- GitHub — Uses Redis extensively for caching repository metadata, session storage, and rate limiting. Open-sourced their tooling around it.
- Lyft — DynamoDB as primary store for many microservices, with Redis (ElastiCache) in front for hot reads and geospatial indexing.
Practice saying it out loud
- Q1When would you choose Redis over DynamoDB (or vice versa) for a key-value workload?
- Q2You're seeing occasional 'slow log' entries on a Redis instance that's only at 60% memory. What's likely going on and how would you diagnose it?
- Q3Design a rate limiter that allows 100 requests per minute per user. How would you implement it on top of a key-value store?
- Q4Your DynamoDB table is throttled despite provisioned capacity being high. The hot partition key is `user_id` of a celebrity. How do you fix it?
- Q5Why is Redis single-threaded, and how does it still achieve >100k ops/sec on a single instance?
Further reading & references
Core explanations are original NO CAP material. External references are provided for deeper study and standards.
What next?
Mark as understood once the mental model clicks.
Next recommended
Document Stores