Core Concepts:
1) short to long, long to shorting - asking encoding
2) http redirect concept
3) High Read Traffic - Horizontal Scaling
4) shortUrl unique - expect key sharding, means non-sql key
5) low latency - redis
1. Design a URL Shortener like Bitly
Question: How would you design a scalable URL-shortening service?
Answer:
Requirements
Convert long URL → short URL.
Redirect short URL → original URL.
High read traffic, low latency.
Short links should be unique.
Architecture
Client
↓
Load Balancer
↓
API Servers
↓
Redis Cache
↓
DatabaseAPIs
POST /shorten→ returns short URLGET /{shortCode}→ HTTP redirect
Database
short_code | long_url | created_at | expiryShort-code generation
Generate a unique numeric ID.
Encode it using Base62 (
a-zA-Z0-9).Example:
125789 → 8KxP
Scaling
Redis caches frequently accessed URLs.
Database is sharded using
short_code.Multiple stateless API servers behind load balancer.
Read replicas handle heavy read traffic.
Important interview point:
The system is read-heavy, so caching and horizontal scaling are critical.
2. Design a Distributed Rate Limiter
Question: How would you design a rate limiter for an API such as 100 requests/minute/user?
Answer:
Architecture
Client
↓
API Gateway
↓
Rate Limiter
↓
Application ServicesUse Redis as the shared distributed state store.
For each user:
Key: rate_limit:user123
Value: request count
TTL: 60 secondsWhen a request arrives:
Redis
INCRthe counter.If first request, set TTL = 60 seconds.
If count ≤ 100 → allow request.
If count > 100 → return
HTTP 429 Too Many Requests.
Why Redis?
Very fast.
Atomic
INCR.Shared state across multiple API servers.
TTL automatically resets the window.
Scaling
┌─ API Server 1 ─┐
Client → LB ─┼─ API Server 2 ─┼→ Redis Cluster
└─ API Server 3 ─┘For more sophisticated traffic control, use Token Bucket or Sliding Window algorithms instead of a simple fixed window.
-----------------------
Sure. For a URL Shortener, the most important part to understand is how the short URL gets converted back to the long URL, and where caching fits.
1. Basic flow
Suppose the user has:
Long URL:
https://www.amazon.in/products/mobile/samsung-galaxy-s24-ultra?id=12345We generate:
Short URL:
https://bit.ly/aB72xWhen someone clicks https://bit.ly/aB72x:
Browser
↓
GET /aB72x
↓
Load Balancer
↓
URL Service
↓
Redis Cache
↓
Database (if cache miss)
↓
Long URL
↓
HTTP 301/302 Redirect
↓
Amazon Long URL2. Where is the long URL stored?
Database:
| short_code | long_url |
|---|---|
| aB72x | https://amazon.in/products/mobile/... |
The short code is the key.
Conceptually:
Map<String, String>
"aB72x" → "https://amazon.in/products/mobile/..."But in a distributed system, we don't keep this only in application memory. We use a persistent database.
3. Why Redis cache?
Imagine aB72x is clicked 1 million times.
Without cache:
1 million requests
↓
1 million DB queriesThat's expensive and creates database pressure.
With Redis:
1 million requests
↓
Redis
↓
Long URLOnly the first request may need the database.
Request 1
↓
Redis MISS
↓
Database
↓
Long URL
↓
Store in RedisThen:
Request 2
Request 3
Request 4
...
↓
Redis HIT
↓
Long URL4. What exactly is cached?
Redis could contain:
KEY VALUE
-----------------------------------------------
url:aB72x https://amazon.in/products/...
url:X7pK2 https://flipkart.com/product/...So:
GET url:aB72xreturns:
https://amazon.in/products/mobile/...5. How does redirect happen?
The URL service gets the long URL from Redis:
aB72x
↓
Redis
↓
https://amazon.in/products/mobile/...Then the server responds:
HTTP/1.1 302 Found
Location: https://amazon.in/products/mobile/...The browser receives the Location header and makes another request to the long URL.
So the URL-shortener server doesn't actually fetch Amazon's page.
It simply says:
"Go to this URL."
6. 301 vs 302
This is an important interview question.
301 — Permanent Redirect
Short URL → Long URLMeans the redirect is permanent. Browsers/proxies may cache the redirect.
302 — Temporary Redirect
Means the redirect may change later.
For a URL shortener, 302 is often safer if you want the destination to remain changeable.
7. Cache miss vs cache hit
This is the key interview explanation:
Request /aB72x
↓
Redis
/ \
HIT MISS
↓ ↓
Long URL DB
↓
Long URL
↓
Store in Redis
↓
RedirectInterview answer in one sentence
Redis caches the mapping between short code and long URL, so frequently accessed short URLs can be resolved without hitting the database, reducing latency and database load; once resolved, the service returns a 301/302 response with the long URL in the Location header, and the browser performs the redirect.
-------------------
TinyURL System Design — Database & Scaling Class Notes
1. Database is a critical HLD decision
After defining the APIs and major components, the next important question is:
How will I store the data, and how will the database scale?
There are two major choices:
SQL
NoSQL
General rule of thumb
| Requirement | Preferred DB |
|---|---|
| Strong consistency + ACID transactions | SQL |
| Very high scalability + availability | NoSQL |
| Complex joins/relationships | SQL |
| Simple key-based lookups | NoSQL |
| Eventual consistency is acceptable | NoSQL |
For TinyURL, NoSQL is a good choice because the primary operation is a simple lookup:
short_url → long_urlWe don't need complex joins or transactions.
2. Start with API → Query → Schema
A very important system-design technique:
API
↓
Queries
↓
Schema
↓
DatabaseDon't start by saying:
"I'll use MongoDB."
Instead ask:
What queries does my application need to perform?
Then design the schema around those queries.
3. TinyURL APIs
Create short URL
POST /shortenInput:
longUrl = https://example.com/very/long/url/...Output:
shortUrl = https://tinyurl.com/aB72xThis requires an INSERT.
long URL → short URLRedirect API
GET /aB72xThe system needs to find:
aB72x → https://example.com/very/long/url/...This is a READ operation.
Therefore the most important lookup key is:
short_url4. TinyURL Schema
A simple NoSQL document could look like:
{
"short_url": "aB72x",
"long_url": "https://example.com/very/long/url",
"created_at": "2026-08-26T10:00:00",
"expiry_at": "2036-08-26T10:00:00"
}Think of it as:
short_url → long_urlwith additional metadata.
5. Why short_url should be a key
Our most common query is:
GET /aB72xSo internally:
Find document WHERE short_url = "aB72x"Therefore:
short_url = Primary KeyThis gives efficient lookup.
Instead of scanning millions of records:
1 → 2 → 3 → 4 → ... → 10 millionthe database can directly locate the partition containing aB72x.
6. Why do we need timestamp / expiry?
Suppose TinyURL keeps links for only 10 years.
Eventually we need to delete expired URLs.
For example:
short_url | created_at | expiry_at
---------------------------------------------
aB72x | 2020 | 2030
xY72p | 2025 | 2035
kL92q | 2026 | 2036A periodic cleanup job can find expired records.
For example:
DELETE URLs WHERE expiry_at < current_timeBut there's an important scaling problem.
7. Why expiry_at needs an index/key
Imagine we have:
1 billion URLsIf expiry_at isn't indexed, the cleanup job may have to scan the entire database:
1 billion records
↓
Full table scan
↓
Very expensiveInstead, we want efficient access to records based on expiration time.
So we can maintain an appropriate secondary index / sorted access path on expiry_at, depending on the NoSQL database being used.
Conceptually:
Primary key:
short_url
Secondary access path:
expiry_atThen:
Find URLs where expiry_at < NOW()is much more efficient.
8. Why NoSQL for TinyURL?
Imagine traffic becomes huge:
1 million requests/secA single database server won't handle this comfortably.
With a distributed NoSQL database such as DynamoDB/Cassandra-style systems, data can be distributed across multiple partitions.
Conceptually:
NoSQL Cluster
/ | \
/ | \
Partition 1 Partition 2 Partition 3
URLs URLs URLsThe database distributes the data automatically according to its partitioning strategy.
This makes horizontal scaling easier.
9. SQL vs NoSQL scaling
With a traditional SQL database:
MySQL
↓
One server
↓
More traffic
↓
Server load ↑You can scale vertically:
8 CPU → 32 CPU → 64 CPUBut there are limits.
Eventually you need techniques such as:
Read replicas
Sharding
Partitioning
Routing
Replication
These can become application/infrastructure complexity.
10. NoSQL horizontal scaling
With a distributed NoSQL database:
NoSQL
↓
┌────────┼────────┐
↓ ↓ ↓
Node 1 Node 2 Node 3As data and traffic grow:
3 nodes
↓
10 nodes
↓
100 nodesThe database system can distribute partitions across the cluster.
Important interview correction: Don't say "NoSQL automatically solves scaling." Say:
"Many distributed NoSQL databases provide built-in partitioning and replication, which makes horizontal scaling easier."
11. Availability vs Consistency
TinyURL doesn't necessarily require every read to immediately see the latest state.
Suppose:
User creates:
aB72x → URL-AFor a very short period, another replica might not have the latest value.
If the system is designed to tolerate this, eventual consistency can be acceptable.
The priority becomes:
Availability + Scalabilityrather than strict consistency everywhere.
12. Hot Partition Problem
This is a very important interview concept.
Suppose this short URL becomes extremely popular:
/aB72xImagine:
10 million requests
↓
aB72x
↓
Same partitionNow one database partition receives disproportionately high traffic.
This is called:
Hot partition
The problem isn't necessarily total database capacity.
The problem is:
One partition is overloaded
while other partitions are underutilized.13. Cache solves the hot-key problem
This is where Redis comes in.
Instead of:
10 million requests
↓
Database
↓
aB72x partitionwe put a cache in front:
10 million requests
↓
Redis
↓
https://example.com/...The database may only receive a small number of requests.
14. Cache HIT
First request:
GET /aB72x
↓
Redis
↓
MISS
↓
Database
↓
Long URL
↓
Store in RedisNext requests:
GET /aB72x
↓
Redis
↓
HIT
↓
Long URL
↓
302 RedirectSo:
Database load ↓
Latency ↓
Hot partition pressure ↓15. Complete TinyURL architecture
Client
|
↓
Load Balancer
|
↓
URL Service
/ \
/ \
Redis Database
Cache NoSQL
\ /
\ /
Long URL
|
↓
HTTP 301/302
|
↓
Original WebsiteRead path
GET /aB72x
↓
API Server
↓
Redis
/ \
HIT MISS
↓ ↓
URL DB
↓ ↓
↓ Redis SET
└───→ ↓
301/302
↓
Long URL16. Interview-ready answer
If the interviewer asks:
"How would you design the database for TinyURL?"
A strong answer is:
"First, I derive the schema from the APIs and access patterns. The main read query is resolving a short URL to its long URL, so
short_urlshould be the primary partition key. I would store the long URL along with creation and expiry timestamps. Since TinyURL has simple key-value access patterns, very high scale, and can tolerate eventual consistency in appropriate areas, I would consider a distributed NoSQL database. For expiry cleanup, I need an efficient access path on the expiry timestamp rather than scanning the entire dataset. Finally, I would put Redis in front of the database to cache popular short-to-long URL mappings and prevent hot partitions and excessive database load."
Remember this interview formula
API
↓
Queries
↓
Schema
↓
Partition Key
↓
Index
↓
Scaling
↓
Cache
↓
Hot Partition ProtectionThis API → Query → Schema → Partition Key → Scaling → Cache approach is extremely useful for almost every system-design interview.
TinyURL — Designing the Short URL
Ramesh Choice for production: Approach 2
TinyURL — Short URL Generation: 2 Approaches
| Concept | Approach 1: Hash + Collision Handling | Approach 2: Unique ID + Base62 |
|---|---|---|
| Basic idea | Hash the long URL and take a few characters | Generate a unique numeric ID and encode it |
| Flow | Long URL → Hash → Short Code | Unique ID → Base62 → Short Code |
| Example | Long URL → CRC32 → ab5ofd43 → ab5of | 123456789 → Base62 → 8M0kX |
| Uniqueness | Not guaranteed because hashes can collide | Guaranteed if the ID is unique |
| Collision handling | Required | Generally not required |
| Collision solution | Change input: hash(URL + salt) and retry | Generate another unique ID |
| Database check | Check whether generated short code already exists | Usually unnecessary for collision, but DB uniqueness constraint is still recommended |
| Short code | Derived from URL content | Derived from unique ID |
| Same URL twice | Can produce the same short code if same input is hashed | Can produce different codes if a new ID is generated each time |
| Distributed system | Collision handling can become more complicated | Need a distributed unique-ID generation strategy |
| Scalability | Good, but collision probability increases with truncation | Very good |
| Implementation complexity | Medium | Relatively simple |
| Main challenge | Collision management | Distributed ID generation |
| Typical production choice | Possible, but requires careful design | Common and easier to scale |
Approach 1 — Hash
Long URL
↓
CRC32 / Hash
↓
Take first 5 characters
↓
Check DB
↓
Collision?
├── Yes → Change input/salt → Hash again
└── No → Store mappingApproach 2 — Unique ID + Base62
Generate Unique ID
↓
123456789
↓
Base62 Encode
↓
8M0kX
↓
Store:
8M0kX → Long URL⭐ Interview takeaway
Hash approach: Main problem = collision.
ID + Base62 approach: Main problem = distributed unique ID generation.
For a large-scale TinyURL system, Unique ID + Base62 is generally the cleaner approach because uniqueness is easier to guarantee and the database lookup is naturally shortCode → longURL.
Common Approach — ID + Base62
A very popular production approach is:
Unique ID ↓ Base62 Encoding ↓ Short Code
Example:
Database ID 123456789 ↓ Base62 ↓ 8M0kX
This avoids the need to hash the entire long URL.
The fundamental mapping becomes:
123456789 ↓ 8M0kX ↓ Database lookup ↓ Long URL
This approach is often easier to reason about for uniqueness and scalability.
10. Complete TinyURL Design
LONG URL ↓ Short URL Generator ↓ ┌──────────────────────┐ │ Hash / Unique ID │ │ + Base62 │ └──────────────────────┘ ↓ Short Code ↓ Database ┌──────────────────────┐ │ short → long URL │ └──────────────────────┘ ↓ Return Short URL
When the user clicks:
tinyurl.com/8M0kX ↓ Redis ↓ Cache HIT? / \ YES NO ↓ ↓ Long URL Database ↓ ↓ └─────────┘ ↓ HTTP 301/302 ↓ Long URL
No comments:
Post a Comment