System Design
Case Study: URL Shortener
Requirements
Functional
- Uzun URL → qisqa URL
- Qisqa URL → redirect to original
- Optional: Custom aliases
- Optional: Analytics
Non-Functional
- Low latency (<50ms)
- High availability
- 100M URLs created/month
- 10B redirects/month
API Design
POST /api/shorten
{
"url": "https://example.com/very/long/url",
"customAlias": "my-link" // optional
}
Response:
{
"shortUrl": "https://short.ly/abc123"
}
GET /{shortCode} → Redirect 301
Database Schema
CREATE TABLE urls (
id BIGSERIAL PRIMARY KEY,
short_code VARCHAR(10) UNIQUE,
original_url TEXT,
created_at TIMESTAMP,
expires_at TIMESTAMP,
user_id BIGINT,
click_count BIGINT DEFAULT 0
);
CREATE INDEX idx_short_code ON urls(short_code);
Short Code Generation
Base62 Encoding
const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
function encode(num) {
let result = '';
while (num > 0) {
result = ALPHABET[num % 62] + result;
num = Math.floor(num / 62);
}
return result || '0';
}
// ID 12345 → short code "3D7"
// 7 characters = 62^7 = 3.5 trillion URLs
Architecture
User Request
↓
CDN (cache popular links)
↓
Load Balancer
↓
API Servers (stateless)
↓
Redis Cache (hot URLs)
↓
PostgreSQL (master-slave)
Caching Strategy
async function redirect(shortCode) {
// L1: Check cache
let url = await redis.get(`url:${shortCode}`);
if (!url) {
// L2: Database
url = await db.query('SELECT original_url FROM urls WHERE short_code = ?', [shortCode]);
// Cache for 24h
await redis.set(`url:${shortCode}`, url, { EX: 86400 });
}
// Async analytics
queue.publish('click', { shortCode });
return url;
}
Scaling
100M URLs/month:
- ~40 writes/sec
- Single database handle qiladi
10B redirects/month:
- ~4K reads/sec
- Redis cache (99% hit rate)
- Read replicas
Keyingi dars: Twitter loyihalash.