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 …
LeaderboardSalaryAccount