URL-Shortener System Design: Architekturdiagramm und Abwägungen

Entwirf einen URL-Shortener Schritt für Schritt: Kapazitätszahlen, Erzeugung der Kurzcodes, 301 vs. 302, Caching, Klick-Analytics und ein Architekturdiagramm.

· Gründer & Entwickler

Veröffentlicht am

URL-Shortener System Design: Architekturdiagramm und Abwägungen

Ein URL-Shortener sieht nach einem Wochenendprojekt aus: lange URL rein, kurzer Code raus, beim Klick weiterleiten. Genau deshalb taucht er in so vielen System-Design-Interviews auf. Hinter der kleinen Oberfläche stecken echte Entscheidungen zu ID-Erzeugung, Caching, HTTP-Semantik und Analytics, und jede davon verändert die Architektur.

Dieser Beitrag entwirft einen von Anfang bis Ende. Er beginnt mit den Zahlen, zeigt, wie du Kurzcodes ohne Kollisionen erzeugst, klärt die Frage 301 oder 302 und endet mit einem Architekturdiagramm, das du kopieren und anpassen kannst.

Anforderungen und Zahlen

Funktionale Anforderungen:

  • Einen Kurzlink für eine lange URL anlegen, optional mit eigenem Alias.
  • Einen Kurzlink auf seine lange URL weiterleiten.
  • Klicks pro Link zählen.
  • Links auf Wunsch ablaufen lassen.

Nicht-funktionale Anforderungen: Redirects müssen schnell und hochverfügbar sein, Kurzcodes dürfen nicht der Reihe nach erratbar sein, wenn Links privat sein können, und das System darf nicht zum kostenlosen Weiterleiter für Phishing-Seiten werden.

Eine Überschlagsrechnung macht die Form deutlich:

Annahme Wert Pro Sekunde
Neue Links pro Monat 100 Millionen etwa 40 Schreibvorgänge/s
Redirects pro Monat 10 Milliarden etwa 3.900 Lesevorgänge/s
Verhältnis Lesen zu Schreiben 100:1
Datensatzgröße etwa 500 Byte
Speicher für 5 Jahre 6 Milliarden Links etwa 3 TB

Lesevorgänge übersteigen Schreibvorgänge um zwei Größenordnungen. Der Lesepfad bekommt also Cache, Replikate und Aufmerksamkeit. Der Schreibpfad ist klein und darf einfach bleiben.

Die API

Zwei Endpunkte tragen das Produkt:

POST /links
Content-Type: application/json

{"url": "https://example.com/very/long/path", "alias": "launch", "expiresAt": "2027-01-01T00:00:00Z"}

201 Created
{"code": "launch", "shortUrl": "https://sho.rt/launch"}

GET /launch

302 Found
Location: https://example.com/very/long/path

Halte sie in getrennten Services. Der Redirect-Service ist zustandslos und liest nur, du kannst ihn also unabhängig skalieren und selten deployen.

Den Kurzcode erzeugen

Der Kurzcode ist eine Zahl in Base62 (a–z, A–Z, 0–9). Sieben Zeichen ergeben 62^7, etwa 3,5 Billionen Codes; sechs ergeben etwa 56,8 Milliarden. Bei 100 Millionen Links im Monat reichen sieben Zeichen für Tausende von Jahren.

Die eigentliche Frage ist, woher die Zahl kommt:

Ansatz Funktionsweise Worauf achten
Zähler + Base62 Eine globale Sequenz, kodiert Ein zentraler Engpass; Codes sind fortlaufend und erratbar
ID-Bereiche Ein Allocator gibt jeder API-Instanz einen Block IDs (etwa 100.000) Lücken, wenn eine Instanz stirbt; innerhalb eines Blocks weiter fortlaufend
Hash + Kürzen URL hashen, die ersten 7 Base62-Zeichen behalten Kollisionen; du brauchst Prüfung und Wiederholung
IDs im Snowflake-Stil Zeitstempel + Maschinen-ID + Sequenz in 64 Bit Längere Codes (11 Zeichen in Base62)

Hashing ist verlockend, weil dieselbe URL immer denselben Code bekommt. Der Haken ist das Geburtstagsproblem: Bei 3,5 Billionen möglichen Codes liegt die Wahrscheinlichkeit für mindestens eine Kollision schon nach etwa 2,2 Millionen Links über 50 %. Danach braucht jedes Einfügen eine Eindeutigkeitsprüfung und einen Wiederholungsversuch mit Salt, und aus einem Schreibvorgang wird ein Lesen plus Schreiben.

ID-Bereiche sind der pragmatische Standard. Jede API-Instanz reserviert in einer kleinen Transaktion einen Block beim Allocator und vergibt die Codes dann ohne jede Koordination aus dem Speicher. Wenn du keine fortlaufenden Codes willst, schick die ID vor dem Kodieren durch eine umkehrbare Permutation (zum Beispiel eine Blockchiffre über den ID-Raum). Twitters Snowflake zeigt den anderen Weg: Ein 41-Bit-Zeitstempel, eine 10-Bit-Maschinen-ID und eine 12-Bit-Sequenz ergeben eindeutige 64-Bit-IDs ganz ohne zentralen Allocator, um den Preis längerer Codes.

Eigene Aliasse umgehen den Generator. Speichere sie in derselben Tabelle und verlass dich auf einen Unique-Constraint auf der Code-Spalte, um Duplikate abzulehnen.

301 oder 302?

RFC 9110 definiert die Redirect-Codes. Der Unterschied, der hier zählt, ist das Caching: Eine 301-Antwort (Moved Permanently) ist heuristisch cachebar, Browser können sie sich also merken und beim nächsten Klick deinen Server überspringen. Ein 302 (Found) ist vorübergehend und wird nicht gecacht, außer du sagst es ausdrücklich.

  • Nimm 302, wenn jeder Klick gezählt werden soll oder du das Ziel später ändern willst.
  • Nimm 301, wenn sich der Link nie ändert und dir weniger Anfragen wichtiger sind als vollständige Analytics.

307 und 308 sind die methodenerhaltenden Varianten von 302 und 301. Bei normalen Browser-Klicks, also GET-Anfragen, spielt die Wahl zwischen ihnen selten eine Rolle.

Speicher und Caching

Jeder Redirect ist ein Punkt-Lookup nach Code. Jeder Speicher mit schnellen Schlüsselzugriffen funktioniert: ein Key-Value-Store wie DynamoDB oder Cassandra oder Postgres mit dem Code als Primärschlüssel. Sharde nach dem Code, wenn ein Knoten nicht mehr reicht; ein Hash des Codes verteilt die Last gleichmäßig, weil per Design kein Code heißer ist als ein anderer. Die Ausnahme sind populäre Links, und die fängt der Cache ab.

Setz einen Redis-Cache mit Cache-Aside-Lesen vor den Speicher: Code in Redis suchen, beim Miss auf die Datenbank zurückfallen, dann den Cache mit einer TTL füllen. Die Beliebtheit von Links folgt einem Long Tail, daher bedient ein Cache, der nur einen kleinen Teil der Links hält, die meisten Klicks. Der Beitrag zu Caching-Strategien behandelt TTLs, Stampedes und Invalidierung im Detail. Read-Replikate helfen dem Fallback-Pfad; der Beitrag zur Datenbank-Replikation erklärt die Verzögerung, mit der du dann leben musst.

Ein CDN oder Edge-Cache kann die beliebtesten Redirects direkt ausliefern, wenn du einen Cache-Control-Header sendest. Dann beantwortet aber die Edge Klicks, die deine Server nie sehen. Logge entweder an der Edge oder lass Redirects ungecacht.

Klick-Analytics, ohne den Redirect zu bremsen

Schreib nie synchron einen Klickzähler im Redirect-Pfad. Der Redirect-Service veröffentlicht ein kleines Klick-Event (Code, Zeitstempel, Referrer, Land) in ein Log wie Kafka und antwortet sofort. Ein Consumer aggregiert die Zahlen pro Link und Tag und schreibt sie in einen Analytics-Speicher. Hängt die Analytics-Seite hinterher, funktionieren Redirects weiter; das ist dieselbe Entkopplung wie im Beitrag zur ereignisgesteuerten Architektur.

Das Architekturdiagramm

Nutzer erreichen CDN und Load Balancer. GET geht an den Redirect-Service: erst Redis, beim Miss der Link-Speicher. POST geht an die Shorten-API: ID-Bereiche vom Allocator, URL-Prüfung per Reputations-API, dann wird der Link geschrieben. Klicks fließen über Kafka in die Analytics.

13 Komponenten · 12 Verbindungen

In Archboard öffnen
Diagramminhalt
  • User
  • Edge / CDN
  • Load balancer
  • Redirect service
  • Shorten API
  • Redis: code to URL
  • Links store
  • ID allocator
  • URL reputation API
  • Click events
  • Click aggregator
  • Analytics store
  • GET /{code}
  • POST /links
  • 1. lookup
  • 2. on miss
  • insert code and URL
  • ID range
  • check the target
  • click event
  • consume
  • daily counts
Ein URL-Shortener mit getrennten Lese- und Schreibpfaden. Redirects fragen zuerst den Cache, neue Links bekommen IDs aus vorab reservierten Bereichen, und Klick-Events fließen in die Analytics, ohne den Redirect zu bremsen.

So liest du es:

  • Lesepfad (oben). Anfragen für GET /{code} laufen über die Edge und den Load Balancer zum Redirect-Service, der zuerst Redis prüft und den Link-Speicher nur bei einem Miss.
  • Schreibpfad (unten). POST /links geht an die Shorten-API. Sie prüft das Ziel bei einem URL-Reputationsdienst, nimmt IDs aus einem beim Allocator reservierten Bereich, kodiert sie in Base62 und schreibt den Link.
  • Analytics (rechts). Klick-Events sind gestrichelt, weil sie asynchron sind. Der Aggregator macht daraus Tageszahlen.
  • Der Markdown-Block hält die Kapazitätszahlen neben dem Design, damit ein Reviewer sie prüfen kann.

Missbrauch, Ablauf und eigene Aliasse

Ein öffentlicher Shortener ist ein Phishing-Werkzeug, solange du es nicht verhinderst. Prüfe neue Ziele gegen einen Reputationsdienst wie Google Safe Browsing, begrenze das Anlegen von Links pro Konto und IP und halte eine Möglichkeit bereit, einen Code schnell zu sperren. Prüfe alte Links regelmäßig erneut, denn eine saubere Domain kann später bösartig werden.

Für den Ablauf speicherst du expiresAt mit dem Link, prüfst es beim Redirect und löschst abgelaufene Zeilen mit einem Hintergrundjob. Gib für abgelaufene Codes 404 oder 410 zurück, statt weiterzuleiten.

Was ins Diagramm gehört

  • Trenne Lese- und Schreibpfad. Sie skalieren unterschiedlich, und das Diagramm sollte es zeigen.
  • Beschrifte den Redirect-Status (301 oder 302) am Redirect-Service. Er entscheidet, ob die Analytics vollständig sind.
  • Zeig, woher die IDs kommen. „Code erzeugen“ in einem Kasten versteckt die meistdiskutierte Entscheidung des Designs.
  • Zeichne asynchrone Flüsse gestrichelt. Leser sollen auf einen Blick sehen, dass Analytics keinen Redirect bremsen kann.
  • Schreib die Zahlen auf. Ein Design ohne Schreibvorgänge pro Sekunde und Speichergröße lässt sich nicht reviewen.

Das Beispiel für ein System-Design-Diagramm zeigt dieselben Konventionen an einem größeren System.

In Archboard nachbauen

  1. Melde dich mit Google oder GitHub an und leg ein Diagramm an.
  2. Füge aus dem C4-Modell-Pack eine Person für den Nutzer hinzu und aus dem Cloud-Pack ein CDN und einen Cloud-Lastverteiler.
  3. Füge aus dem C4-Modell-Pack vier Container-Nodes hinzu: den Redirect-Service, die Shorten-API, den ID-Allocator und den Klick-Aggregator. Schreib jeweils die Technologie und eine einzeilige Beschreibung dazu.
  4. Füge aus dem Daten-Pack Redis, Kafka und ein Datenlager hinzu, aus dem C4-Modell-Pack eine Datenbank für den Link-Speicher und ein Externes System für die URL-Reputations-API.
  5. Verbinde oben den Lesepfad und unten den Schreibpfad, zeichne die Klick-Events als gestrichelte Kanten und füge einen Markdown-Block aus dem Basis-Pack mit den Kapazitätszahlen hinzu.

Das Beispiel nutzt 13 Diagramm-Nodes und bleibt damit im Free-Plan.

FAQ

Wie lang sollte ein Kurzcode sein? Sieben Base62-Zeichen ergeben etwa 3,5 Billionen Codes, genug für Jahrzehnte bei 100 Millionen Links im Monat. Starte mit sieben und lass die Codes später auf acht wachsen; nichts im Design hängt an einer festen Länge.

Sollte ich die lange URL hashen, um den Code zu bekommen? Nur wenn Deduplizierung wichtiger ist als einfache Schreibvorgänge. Gekürzte Hashes kollidieren früh (ab 2,2 Millionen Links bei 7 Zeichen ist mindestens eine Kollision wahrscheinlicher als keine), also braucht jedes Einfügen eine Prüfung und einen Wiederholungsversuch.

Ist 301 oder 302 besser für einen URL-Shortener? 302, wenn jeder Klick gezählt werden muss oder du das Ziel ändern könntest. 301 lässt Browser den Redirect cachen; das spart Anfragen, verbirgt aber wiederholte Klicks vor deinen Analytics.

Welche Datenbank sollte die Links speichern? Jeder Speicher mit schnellen Lookups über den Primärschlüssel. Postgres reicht bei diesen Zahlen lange; ein Key-Value-Store wird attraktiv, wenn du Schreibvorgänge über viele Knoten sharden musst.

Wie verhindere ich, dass Leute Links erraten? Gib keine fortlaufenden IDs nach außen. Permutiere die ID vor dem Kodieren oder nutze zufällige Codes mit Eindeutigkeitsprüfung, und behandle einen Kurzlink nie als Geheimnis für private Inhalte.

Kostenlos starten — ein Seat, keine Kreditkarte.

Ähnliche Beiträge

← Zurück zum Blog

In einer Minute ein Canvas öffnen

Melden Sie sich mit Google oder GitHub an. Kostenlos für einen Sitzplatz. Upgraden Sie, wenn das Team dazukommt.