The service maps a long URL to a short code and back. Functional requirements: shorten (POST), redirect (GET), custom aliases, expiry, and click analytics. At 100M new URLs per day the machine must sustain ~1,160 writes/sec average and, at a 100:1 read:write ratio, ~115K reads/sec — peaking multiples higher in business hours.
The ID is the core decision. The machine reserves the ID first — a ticket from a distributed ID generator or a DB auto-increment — then encodes it in base62 ([a-zA-Z0-9]), so 7 chars cover 62^7 ≈ 3.5 trillion URLs. No hash-and-check loop needed; the mapping is bijective. Redirects are read-mostly, so the hot path is: check Redis by code, on miss read MySQL, populate cache with TTL, 301 to the long URL.
Components: stateless API servers behind a load balancer; Redis for the read cache; MySQL sharded by code hash for the mapping table; an async worker that consumes a Kafka topic of shorten events to build analytics (per-code click counts, referrers, geo).
Data model: one table, mappings(code PK, long_url, user_id, created_at, expires_at), plus a clicks aggregate table written by the analytics worker. The cache stores code → long_url only; nothing else needs sub-millisecond reads.
Bottlenecks: DB write throughput during bursts (mitigate with batch inserts and a write buffer queue); cache stampede on cold keys (use single-flight or short randomized TTLs); base62 decode must handle 7-char codes with leading zeros correctly — the code is a number, not a string. Analytics must never sit on the redirect path; it is fire-and-forget to Kafka.
Client → LB → API servers
├─ shorten: ID gen → base62 → write MySQL → warm Redis
└─ redirect: Redis hit? → 301 : MySQL → populate cache → 301
Kafka ← async workers → ClickHouse (analytics)