We choose consistency over availability when we need to make a design choice.
Assume write RPS = 10, read QPS = 200.
To store one year of short urls:
10 * 86400 * 365 = 320 M short urls.
Assume we use base 62 (10 digits plus lower and upper case english letters).
Log(320 M) / Log(62) = 5. We need 5 characters for the short url key.
For read QPS 200, that's 5 ms per request, or 1-2 ms per request at peak. We need multiple servers to handle read request.
For write QPS 10, that's 100 ms per request, or 20-40 ms per request at peak. We need one server at non-peak hours and multiple servers at peak hours.
Write:
POST /
payload is long url
response payload contains the short url created.
Read:
GET /
This happens when user visits the website, e.g., bit.ly/
Response is an http redirect to the corresponding long url
320 M pairs of short and long url pairs to store.
http urls have a limit of 2000 characters. Assume the average long url length is 500 characters.
Storage need for one year (5 short characters, 500 long characters, and timestamp of creation):
320 M * (5 + 500 + 8) = 165 GB
Data is small. And due to its highly relational nature, we choose SQL.
See diagram.
Write:
Read:
Short URL generation algorithm
We have a few choices:
Expiration
We have a few choices:
See details in "Detailed component design"
Service failure
Both write and read service may fail. We may need leader-followers architecture for the services. All writes go to the leader, and all reads go to the followers. If a leader fails, the followers will elect a new leader.
With this design, we need carefully store all write requests to a log on each server, so that if leader fails, we can compare the log and identify any discrepancy before making the new leader up running.
Also, consider read-after-write and cross-device read-after-write. The same user's read after write is better be routed via the leader to avoid fake data discrepancy.
DB sharding and replication
One single database can store all the data, but may be slow to process all requests. We can partition based on the beginning characters of the short urls, especially useful when the short url keys are generated randomly, so they can distribute randomly to different partitions. Each partition can act as its own main replica, and as others' secondary replica. Using replica so that if one partition is down, other partition can still be used for read and write.
Load balance
Load balancer should take account for the above server and database scaling.
Larger amount of data
If the data is much larger, we need a very careful sharding and replication database design, as well as distributing traffic load between servers more wisely.
User account
We may add user account system to allow registered users to have their short urls be associated with their account and manage them.