| Users / Requests | 100 Users | 10K Users | 1M Users | 100M Users |
|---|---|---|---|---|
| Requests per second (QPS) | ~100 | ~10,000 | ~1,000,000 | ~100,000,000 |
| Data size | ~10 GB | ~1 TB | ~100 TB | ~10 PB |
| Number of servers | 1-2 | 10-20 | 100-200 | 10,000+ |
| Latency | <1 ms | <5 ms | <10 ms | <20 ms |
| Storage type | SSD local | Distributed SSD | Sharded distributed storage | Multi-region distributed storage |
| Replication | Simple master-slave | Multi-replica for availability | Geo-replication | Global replication with consistency |
Design a key-value store in HLD - Scalability & System Analysis
Start learning this pattern below
Jump into concepts and practice - no test required
At small scale (100 users), the database server CPU and disk I/O are the first bottlenecks because a single server handles all requests and data.
At medium scale (10K to 1M users), the database query throughput and network bandwidth become bottlenecks as requests increase beyond a single server's capacity.
At large scale (100M users), data partitioning and cross-region replication latency become bottlenecks due to massive data size and global distribution.
- Vertical scaling: Upgrade server CPU, RAM, and SSDs for small scale.
- Horizontal scaling: Add more servers behind a load balancer to distribute requests.
- Sharding: Split data by key ranges or hash to distribute storage and load across servers.
- Caching: Use in-memory caches (e.g., Redis) to reduce database load for frequent reads.
- Replication: Use master-slave or multi-master replication for availability and read scaling.
- Consistent hashing: To minimize data movement when scaling out or in.
- Geo-distribution: Deploy data centers closer to users to reduce latency at large scale.
- At 1M QPS, assuming 1KB per request, bandwidth needed is ~1 GB/s (8 Gbps).
- Storage for 100 TB data requires multiple SSD servers; each SSD ~4 TB, so ~25 servers minimum.
- Each server handles ~5,000 QPS; for 1M QPS, need ~200 servers.
- Network infrastructure must support high throughput and low latency.
- Replication doubles storage and bandwidth needs.
Start by clarifying requirements: data size, read/write ratio, latency needs.
Discuss simple design first, then identify bottlenecks as scale grows.
Explain how each scaling solution addresses specific bottlenecks.
Use real numbers to justify design choices and trade-offs.
Your database handles 1000 QPS. Traffic grows 10x to 10,000 QPS. What do you do first?
Answer: Add read replicas and implement caching to reduce load on the primary database before scaling vertically or sharding.
Practice
Solution
Step 1: Understand key-value store basics
A key-value store saves data as pairs where each key maps to a value for fast retrieval.Step 2: Compare with other storage types
Unlike relational databases, key-value stores do not support complex queries or file storage.Final Answer:
To save data as pairs for quick lookup -> Option CQuick Check:
Key-value store = data pairs [OK]
- Confusing key-value store with relational database
- Thinking it handles large files natively
- Assuming it manages user sessions directly
Solution
Step 1: Identify operation purpose
Adding or updating a value requires an operation that sets the value for a key.Step 2: Match operation names
"put" is commonly used to insert or update key-value pairs; "get" retrieves, "delete" removes, "exists" checks presence.Final Answer:
put(key, value) -> Option BQuick Check:
Put = add/update [OK]
- Using get to add data
- Confusing delete with update
- Using exists to insert values
store = {}
store.put('a', 1)
store.put('b', 2)
store.put('a', 3)
value = store.get('a')
What is the value of value after these operations?Solution
Step 1: Track put operations
First, key 'a' is set to 1, then 'b' to 2, then 'a' is updated to 3, overwriting previous value.Step 2: Retrieve the value for 'a'
The last value assigned to 'a' is 3, so store.get('a') returns 3.Final Answer:
3 -> Option AQuick Check:
Last put for 'a' = 3 [OK]
- Assuming first value stays after update
- Confusing keys 'a' and 'b'
- Thinking get returns None if key exists
store = {}
def get_value(key):
if key in store:
return store[key]
else:
return None
store.put('x', 10)
print(get_value('x'))
What is the main issue preventing this code from working correctly?Solution
Step 1: Check dictionary operations
Python dictionaries do not have a put method; they use assignment like store[key] = value.Step 2: Identify error cause
Calling store.put('x', 10) will cause an AttributeError because put is undefined.Final Answer:
The put method is not defined for the dictionary -> Option AQuick Check:
Dicts use assignment, not put [OK]
- Assuming put exists on dict
- Ignoring error from undefined method
- Thinking get_value logic is faulty
Solution
Step 1: Understand scalability needs
Handling millions of requests requires distributing load and data to avoid bottlenecks.Step 2: Evaluate design options
A single in-memory dictionary or disk-based DB limits capacity; relational DB with joins is slow for key-value access. Consistent hashing partitions data evenly across servers, enabling horizontal scaling.Final Answer:
Partition data across multiple servers using consistent hashing -> Option DQuick Check:
Consistent hashing = scalable partitioning [OK]
- Relying on single server limits throughput
- Using disk-based DB slows access
- Choosing relational DB for simple key-value
