A URL shortener is a tiny write path and a very hot read path: one insert per shorten, one redirect per click — with clicks outnumbering shortens by thousands to one. The design follows the ratio.
{"url": "https://..."}. The write path begins: validate the URL (scheme, host), then produce a short key. Key generation is where the first decision lives — base62 encoding of a counter, a Snowflake ID, or a random string with collision retry.xK9f2Qp and must now prove it's free: the insert itself is the proof. On a unique-key violation (two generators racing, or randomness colliding — ~1 in a billion at 50M keys), the app retries with a new key; with an ID-pool or counter scheme, collisions are impossible by construction.(key, url, created_at, owner) is inserted — a single PK insert, ~1–5ms. The key is the primary key, and the read path will be a point lookup on exactly that key. Some designs also store the URL's hash for deduplication ("this URL is already shortened — return the existing key"); dedup costs an index and a second query, and is optional.SET key → url (with a long TTL, or no expiry — short links don't rot). Now the read path can serve the most common case without touching the database.GET /xK9f2Qp. The edge (CDN) or app does one Redis lookup, ~0.5ms, hit rate typically 95%+. It responds with a redirect. The browser follows: two round trips for the user, one cache read for the system. This is the whole performance story — the read path is a cache lookup plus a 3xx.301 Moved Permanently (browsers and CDNs cache this aggressively — which is what you want for share links) or 308 Permanent Redirect (preserves the HTTP method for API clients that POST links). The choice is a caching policy, not a formality: a 301 misused for a transient URL is how users get stuck on dead links.POST /shorten {url} # → key = base62(generate())
INSERT short_urls(key, url) # unique violation → retry with new key
SETEX redirect:{key} url # warm the cache
GET /:key # Redis GET redirect:{key}
# hit → 301 Location: url
# miss → SELECT url WHERE key = :key → SET → 301The cost ledger: one insert + one cache write per shorten; one cache read per click with a ~1ms tail to the DB on miss. At 100M clicks/day that's ~1,200 cache reads/second — a single Redis node's idle capacity. The database only ever sees writes and the 5% read misses, which is precisely why the system stays trivially small: the architecture is a ratio, not a pile of machines.