When an interviewer asks you to design a key‑value store, they’re looking for how you translate a simple API into a reliable, scalable system. The conversation usually starts with requirements gathering, moves to a high‑level diagram, then dives into the parts that get tricky – persistence, sharding, replication, and consistency. Below is a practical walk‑through you can follow in a real interview.
1. Clarify Requirements
Functional
- PUT(key, value) – store or overwrite a value.
- GET(key) – retrieve the latest value.
- DELETE(key) – remove a key.
- Optional: LIST(prefix) for range queries, CAS (compare‑and‑set) for atomic updates.
Non‑Functional
| Requirement | Typical interview focus |
|---|---|
| Latency | Millisecond‑level reads, sub‑10 ms writes. |
| Throughput | Depends on traffic, but you should discuss how to scale horizontally. |
| Durability | Data must survive node failures. |
| Availability | Aim for >99.9 % uptime; discuss trade‑offs with consistency. |
| Scalability | Ability to add nodes without major re‑balancing. |
| Security | Authentication/authorization is optional but worth mentioning. |
Ask the interviewer which of these matter most for the scenario. For a generic store, durability and availability are usually top priorities.
2. Define the Core API
// Simple Go‑like signature
func Put(key string, value []byte) error
func Get(key string) ([]byte, error)
func Delete(key string) error
If the role involves client‑side libraries, mention language bindings and serialization formats (JSON, protobuf, etc.).
3. High‑Level Architecture
Client → Load Balancer → API Layer → Routing Layer →
├─ In‑Memory Cache (e.g., Redis) │
└─ Storage Nodes (sharded) │
├─ Write‑Ahead Log (WAL) │
└─ Persistent Store (SSD) │
- Load Balancer distributes requests across API instances.
- API Layer handles authentication, request parsing, and forwards to the routing layer.
- Routing Layer decides which shard owns the key (consistent hashing is a common choice).
- Cache provides fast reads for hot keys.
- Storage Nodes store data on disk, protected by a write‑ahead log for crash recovery.
Why start simple?
Begin with a single monolithic node that does all of the above. Once the baseline works, you can split responsibilities and add replication.
4. Deep Dive: Sharding & Routing
Consistent Hashing
- Map each node to points on a hash ring.
- Hash the key and locate the nearest node clockwise.
- Adding/removing a node only moves a fraction of keys.
Trade‑off: Uneven distribution can happen; virtual nodes (multiple points per physical node) smooth it out.
Re‑sharding
When capacity grows, you’ll need to move data. Discuss a background re‑balancing job that streams affected keys to new nodes while keeping the old node serving reads.
5. Persistence Layer
Write‑Ahead Log (WAL)
- Append the mutation (PUT/DELETE) to a sequential log on durable storage.
- Flush to disk before acknowledging the client.
- Periodically compact the log to discard superseded entries.
Data Files
- Store key‑value pairs in sorted‑string tables (SSTables) similar to LSM‑tree designs.
- Use a Bloom filter per file to avoid unnecessary disk reads.
Failure handling: On crash, replay the WAL to rebuild the in‑memory index.
6. Replication & Consistency
Primary‑Backup Model
- Each shard has a leader (primary) and one or more followers.
- Writes go to the leader, which replicates to followers via synchronous or asynchronous replication.
- Reads can be served from any replica if you accept eventual consistency.
Consistency Models
- Strong consistency: Read after write returns the latest value (requires quorum writes/reads).
- Eventual consistency: Faster writes, but stale reads possible.
Explain why an interview might prefer one over the other (e.g., banking needs strong, caching layer tolerates eventual).
7. Handling Hot Keys & Load Balancing
- Hot key mitigation: Use request‑level sharding (e.g., hash the request ID) or move hot keys to dedicated nodes.
- Back‑pressure: API layer can return 429 when storage nodes are saturated.
8. Common Follow‑Up Questions
| Question | Angle the interviewer is probing |
|---|---|
| How would you add TTL support? | Adding a background expiration thread and storing expiration timestamps in the index. |
| What if the client needs atomic increments? | Implement a CAS loop or a dedicated counter service. |
| How do you ensure durability across data‑center failures? | Multi‑region replication, quorum writes, and a global consensus protocol (e.g., Raft). |
| Can you support range queries? | Store keys in a sorted structure (B‑tree or LSM) and expose a SCAN(start, end) API. |
| What monitoring metrics would you expose? | Latency histograms, request rates, cache hit ratio, disk I/O, and replication lag. |
Answer each follow‑up by extending the existing diagram rather than starting from scratch.
9. Sample Answer (45‑90 seconds)
"The core of a key‑value store is a simple
PUT/GET/DELETEAPI. I’d start with a single node that writes to a durable write‑ahead log and stores data in sorted files, using a Bloom filter for fast look‑ups. To scale, I’d partition the key space with consistent hashing, adding a routing layer that maps keys to shards. Each shard would have a primary‑backup replication model; writes go to the leader and are replicated synchronously for strong consistency, while reads can be served from any replica for low latency. A small in‑memory cache sits in front of the storage nodes to handle hot keys. For durability, the WAL is replayed on restart, and periodic compaction keeps the on‑disk size manageable. If we need to support TTL or atomic increments, I’d add a timestamp field to each entry and expose a CAS‑basedINCRoperation that runs on the leader. Monitoring would include latency percentiles, cache hit ratio, and replication lag."
10. How to practice this
- Sketch the diagram on paper before the interview, then explain each component in a sentence.
- Run a mock interview and answer the core API question aloud; use Call Assistant to capture the flow and keep follow‑ups on track.
- Pick a follow‑up (e.g., TTL) and write a quick pseudo‑code snippet to show how you’d extend the design.
FAQ
- What is the simplest way to make a key‑value store fault‑tolerant? Use a primary‑backup replication scheme with synchronous writes; the backup can take over if the primary fails.
- How does consistent hashing avoid massive data movement? By mapping nodes to points on a ring, only the keys that fall between the old and new node positions need to be moved when the cluster changes size.
- When would you choose eventual consistency over strong consistency? When read latency is critical and occasional stale reads are acceptable, such as in a caching layer or analytics dashboard.
- What monitoring should you set up for a production key‑value store? Track request latency, error rates, cache hit ratio, disk I/O, and replication lag to quickly detect performance or reliability issues.
Frequently asked questions
What is the simplest way to make a key-value store fault-tolerant?
Use a primary‑backup replication scheme with synchronous writes; the backup can take over if the primary fails.
How does consistent hashing avoid massive data movement?
By mapping nodes to points on a ring, only the keys that fall between the old and new node positions need to be moved when the cluster changes size.
When would you choose eventual consistency over strong consistency?
When read latency is critical and occasional stale reads are acceptable, such as in a caching layer or analytics dashboard.
What monitoring should you set up for a production key-value store?
Track request latency, error rates, cache hit ratio, disk I/O, and replication lag to quickly detect performance or reliability issues.
#system design#key-value store#scalability#consistency#interview prep#a key-value store