Design a URL Shortener
Problem Design a URL-shortening service: given a long URL produce a short unique alias, and given the alias redirect to the original URL at very high read volume.
Functional requirements
- Create a short link from a long URL; return the alias.
- Redirect alias → original URL (HTTP 301/302).
- Optional custom aliases chosen by the user.
- Optional expiry (TTL) on a link.
- Click analytics per alias.
Non-functional requirements
- ~100M new links/day (~1,200 writes/sec average, ~5k/sec peak).
- Read:write ratio ~100:1 → ~10k redirects/sec average, ~50k/sec peak.
- Redirect p99 < 50 ms — this is the hot path and dominates the design.
- 5-year retention: 100M/day x 365 x 5 ≈ 180B links; at ~500 bytes/row ≈ 90 TB, so storage is sharded from day one.
- 99.99% availability for redirects; link creation can tolerate lower.
Key components
- Write path: an API service that generates the alias and persists alias → long_url.
- ID generation: either a distributed counter (Snowflake-style / Redis INCR / per-host ID ranges) base62-encoded, or a hash (MD5/SHA-256) of the URL truncated to 7 chars with collision-check-and-retry.
- Storage: a key-value store optimized for point lookups, sharded on hash(alias). The mapping is immutable, which makes it trivially cacheable.
- Cache: Redis in front of the KV store; a CDN/edge layer can serve redirects for the hottest aliases without touching origin.
- Analytics: emit a click event to Kafka and aggregate offline — never write a counter synchronously on the redirect path.
Deep dives / trade-offs
- Counter vs hash: base62 of an auto-increment ID guarantees uniqueness with no collision check, but leaks total volume, is sequential/enumerable, and makes the ID generator a single point of contention. Hashing removes the central counter but needs collision detection and re-salting; it also makes the same URL map to the same alias, which is either a feature (dedup) or a bug (per-user analytics).
- 7 characters of base62 = 62^7 ≈ 3.5 trillion aliases — show the arithmetic and when you need an 8th character.
- 301 vs 302: 301 is cached by the browser, cutting origin load dramatically but destroying per-click analytics and making a link impossible to re-point. 302 preserves both at the cost of every click hitting your service.
- Cache strategy: the access distribution is heavily Zipfian — a small working set serves most traffic. Discuss LRU sizing, cache-aside vs read-through, and how a cold cache after a deploy can stampede the KV store.
- Expiry and reclamation: lazy deletion on read vs a background sweeper, and whether expired aliases may be recycled (they should not — link rot vs security).
asked …