URL shortener
Not climbed yetModel answer
Two endpoints:
POST /linkswith the long URL (and an optional custom alias and expiry) returns the short code;GET /{code}redirects to the long URL.One main table:
links(code PRIMARY KEY, long_url, owner_id, created_at, expires_at). Lookups are bycode, so the primary key is the only index the hot path needs.Say the shape out loud: reads (redirects) far outnumber writes (new links), maybe 100 to 1, so the design is about fast, cheap reads. Validate the long URL and block known-malicious domains, since shorteners are a favourite tool for phishing.
Model answer
- Encode a unique number: take an id from a counter and write it in base 62 (
a-z,A-Z,0-9). Seven characters give 62^7, about 3.5 trillion codes. To avoid one counter becoming a bottleneck, each server reserves a range of numbers (1 to 10,000, then 10,001 to 20,000) and hands them out from memory. - Random codes: generate 7 random base-62 characters and insert with a unique constraint; on the rare collision, try again. No coordination at all.
- Hashing the URL (first 7 characters of a hash) makes the same URL always get the same code, but needs collision handling anyway.
Counter-based codes are guessable in sequence, which lets people walk through every link; random codes or shuffling the counter's bits avoid that.
- Encode a unique number: take an id from a counter and write it in base 62 (
Model answer
Cache the code-to-URL mapping. Links almost never change, so a Redis cache in front of the database (or an in-process cache on each server for the hottest links) serves nearly all lookups from memory.
Go further out: serve redirects from a CDN or edge that caches the redirect response, so most requests never reach your servers.
Choose the status code knowingly: 301 (permanent) lets browsers cache the redirect, so repeat clicks never reach you at all, but you stop seeing them. 302/307 sends every click through you, which you need for analytics and for links that can be edited or expire.
Behind the cache, the database can be read replicas or a key-value store partitioned by code.
Model answer
Never write to the database inside the redirect. Return the redirect first, and emit a click event (code, time, country from IP, user agent) to a queue or log such as Kafka, asynchronously.
A stream processor or a batch job reads the events and aggregates them into counts per link per day, written to an analytics store (ClickHouse, BigQuery, or a counts table). Dashboards read the aggregates, not the raw events.
Losing a few events in a crash is usually acceptable for analytics, which is exactly why it is safe to take it off the critical path. Filter bots before counting, or the numbers mean little.
