Database Design Mistakes I Made So You Don't Have To \u2014 TXT1.ai

March 2026 · 15 min read · 3,636 words · Last Updated: March 31, 2026Advanced

💡 Key Takeaways

  • The "I'll Just Use UUIDs Everywhere" Disaster
  • Premature Normalization: When Third Normal Form Becomes Your Enemy
  • The NULL Nightmare: When Optional Becomes Impossible
  • Index Overload: When More Isn't Better
Ich werde diesen Experten-Blogartikel für Sie als umfassendes HTML-Dokument aus der Perspektive einer bestimmten Persona schreiben.

Vor drei Jahren habe ich zugesehen, wie die Datenbank unseres Startups um 2:47 Uhr am Black Friday zum Stillstand kam. Wir hatten 50.000 gleichzeitige Nutzer, 2 Millionen Dollar an ausstehenden Transaktionen und eine Abfrage, die 45 Sekunden benötigte, um die Produktverfügbarkeit zurückzugeben. Unser CTO schrie in Slack. Unsere Investoren riefen an. Und ich starrte auf ein Schema, das ich sechs Monate zuvor entworfen hatte und erkannte, dass jede "clevere" Entscheidung, die ich getroffen hatte, uns nun ungefähr 8.000 Dollar pro Minute an entgangenem Umsatz kostete.

💡 Wichtigste Erkenntnisse

  • Die "Ich benutze einfach überall UUIDs" Katastrophe
  • Vorzeitige Normalisierung: Wenn die dritte Normalform zu Ihrem Feind wird
  • Der NULL-Albtraum: Wenn Optional unmöglich wird
  • Indexüberlastung: Wenn mehr nicht besser ist

Ich bin Marcus Chen und habe die letzten zwölf Jahre als Datenbankarchitekt gearbeitet, mit allem von kämpferischen SaaS-Startups bis zu Fortune 500-Unternehmen. Ich habe Schemata für Systeme entworfen, die 500 Millionen tägliche Transaktionen verarbeiten, Abfragen optimiert, die 200 ms bei kritischen Pfaden eingespart haben, und ja—ich habe fast jeden Datenbankdesignfehler gemacht, den es gibt. Der Vorfall am Black Friday? Er hat mir mehr über Datenbankdesign beigebracht als mein gesamtes Informatikstudium.

Heute bin ich der leitende Datenbankarchitekt bei TXT1.ai, wo wir jährlich über 3 Milliarden Textnachrichten über unsere KI-gestützte Kommunikationsplattform verarbeiten. Aber ich bin hierher gekommen, indem ich aus meinen Fehlern gelernt habe, und ich möchte die kostspieligen Lektionen teilen, die ich gelernt habe, damit Sie die Panikattacken um 2 Uhr morgens und die wütenden Anrufe von Investoren überspringen können.

Die "Ich benutze einfach überall UUIDs" Katastrophe

Ich fange mit dem an, was ich meinen $40.000 Fehler nenne. Im Jahr 2019 entwarf ich ein Customer Relationship Management-System für ein mittelständisches E-Commerce-Unternehmen. Ich hatte gerade einen Blogbeitrag gelesen, der besagte, dass UUIDs der "moderne" Weg seien, um Primärschlüssel zu behandeln—keine Auto-Inkrement-Integer mehr, keine sequenziellen ID-Expositionen mehr, perfekt für verteilte Systeme. Ich war überzeugt.

Also machte ich jeden einzelnen Primärschlüssel im System zu einem UUID. Benutzer-Tabelle? UUID. Bestellungen-Tabelle? UUID. Bestellpositionen? Sie haben es erraten—UUID. Ich fühlte mich wie ein Genie. Das Schema sah sauber aus, es gab keine sequentiellen ID-Schwachstellen, und ich konnte IDs clientseitig generieren, wenn nötig. Was könnte schon schiefgehen?

Alles. Absolut alles ging schief.

Innerhalb von sechs Monaten war unsere Datenbankgröße auf 340 GB angewachsen, obwohl sie etwa 180 GB hätte betragen sollen. Die Abfrageleistung nahm Woche für Woche ab. Unsere Indexgrößen waren riesig—der Index der Bestellungen-Tabelle allein war 12 GB groß. Joins zwischen Bestellungen und Positionen, die 50 ms dauern sollten, benötigten 800 ms. Die Datenbank verbrachte enorme Mengen an Zeit mit Disk I/O, und unsere AWS RDS-Kosten hatten sich fast verdoppelt.

Hier ist, was ich auf die harte Tour gelernt habe: UUIDs sind 128 Bits (16 Bytes) im Vergleich zu einem 4-Byte-Integer oder einem 8-Byte-Bigint. Das sind 4x der Speicherkapazität für jeden Primärschlüssel. Aber das eigentliche Problem ist nicht der Speicher—es ist die Indexfragmentierung. UUIDs sind zufällig, was bedeutet, dass jede Einfügung zufällige Schreibvorgänge über Ihre B-Baum-Indizes verursacht. Bei sequentiellen Integers fügen sich neue Zeilen am Ende des Indexes an. Bei UUIDs muss die Datenbank ständig die gesamte Indexstruktur neu ausbalancieren.

Wir haben den Einfluss gemessen: 100.000 Zeilen mit Integer-IDs einzufügen dauerte 8 Sekunden. Die gleiche Operation mit UUIDs dauerte 34 Sekunden. Das ist eine 4,25-fache Leistungsstrafe nur aufgrund der Wahl des Primärschlüssels. Wenn Sie 50.000 Bestellungen pro Tag verarbeiten, summiert sich das schnell.

Die Lösung kostete uns drei Wochen Entwicklungszeit und erforderte eine sorgfältig orchestrierte Migration während eines Wartungsfensters. Wir wechselten zu Bigint-Primärschlüsseln für hochvolumige Tabellen und behielten UUIDs nur für Tabellen, in denen wir wirklich global eindeutige Identifikatoren über verteilte Systeme benötigten—was sich als genau zwei Tabellen von siebenundvierzig herausstellte.

Meine Regel jetzt: Verwenden Sie Auto-Inkrement-Integer oder Bigints für Primärschlüssel, es sei denn, Sie haben einen spezifischen, dokumentierten Grund, UUIDs zu verwenden. Und "es scheint moderner" ist kein dokumentierter Grund.

Vorzeitige Normalisierung: Wenn die dritte Normalform zu Ihrem Feind wird

Frisch von der Universität war ich besessen von Normalisierung. Ich hatte alle Normalformen auswendig gelernt, konnte Codds Regeln im Schlaf aufsagen und glaubte, dass eine richtig normalisierte Datenbank der Höhepunkt des Design-Hochglanzes sei. Also als ich mein erstes Produktionssystem—eine Content-Management-Plattform—entwarf, normalisierte ich alles bis zur dritten Normalform und darüber hinaus.

"Jede 'clevere' Datenbankentscheidung, die Sie heute treffen, ist ein potenzielles Krisenszenario um 2 Uhr morgens in sechs Monaten. Designen Sie für das System, das Sie haben werden, nicht für das System, das Sie wollen."

Ich hatte eine Benutzer-Tabelle, eine Benutzer-Adressen-Tabelle (weil Benutzer mehrere Adressen haben könnten), eine Benutzer-Telefonnummern-Tabelle (mehrere Telefone!), eine Benutzer-Präferenzen-Tabelle, eine Benutzer-Einstellungen-Tabelle und eine Benutzer-Metadaten-Tabelle. Das Laden des Profils eines einzelnen Benutzers erforderte das Joinen von sechs Tabellen. Ich war so stolz darauf, wie "sauber" alles aussah.

Dann haben wir gestartet. Die Benutzerprofilseite—die am häufigsten aufgerufene Seite der gesamten Anwendung—brauchte 1,2 Sekunden zum Laden. Wir machten sechs Joins für jede einzelne Seitenansicht, und mit 10.000 täglich aktiven Nutzern bedeutete das 60.000 Joins pro Tag nur für Profilansichten. Die CPU der Datenbank lag ständig über 70%.

Der Weckruf kam, als unser leitender Entwickler mich beiseite zog und mir den Abfrage-Ausführungsplan zeigte. "Marcus," sagte er, "wir joinen sechs Tabellen, um den Namen, die E-Mail und die Telefonnummer eines Benutzers anzuzeigen. Das ist verrückt." Er hatte recht. Ich hatte für theoretische Reinheit statt für praktische Leistung optimiert.

Wir entnormalisierten strategisch. Die primäre Adresse des Benutzers kam zurück in die Benutzer-Tabelle. Ihre primäre Telefonnummer? Dasselbe. Wir behielten die separaten Tabellen für zusätzliche Adressen und Telefonnummern, aber 94% unserer Nutzer hatten nur eines von beidem. Diese einzelne Änderung reduzierte unsere durchschnittliche Abfragezeit der Profilseite von 1,2 Sekunden auf 180 ms—eine Verbesserung von 85%.

Hier ist, was ich gelernt habe: Normalisierung ist ein Werkzeug, keine Religion. Die dritte Normalform ist ein großartiger Ausgangspunkt, aber die Leistung in der realen Welt erfordert oft strategische Entnormalisierung. Jetzt folge ich dem, was ich die "80/20 Entnormalisierungsregel" nenne—wenn 80% der Abfragen Daten aus mehreren Tabellen benötigen, gehören diese Daten wahrscheinlich in eine Tabelle. Ich messe Abfragemuster in der Produktion und entnormalisiere basierend auf der tatsächlichen Nutzung, nicht auf theoretischer Reinheit.

Der Schlüssel liegt darin, zu wissen, wann man entnormalisieren sollte. Tabellen mit häufigen Lesevorgängen und wenigen Schreibvorgängen sind perfekte Kandidaten. Benutzerprofile, Produktkataloge, Konfigurationsdaten—das sind alles großartige Plätze für die Entnormalisierung. Transaktionstabellen mit hohem Schreibaufkommen? Halten Sie diese normalisiert, um Aktualisierungsanomalien zu vermeiden.

Der NULL-Albtraum: Wenn Optional unmöglich wird

Früher liebte ich nullable Spalten. Sie schienen so flexibel, so entgegenkommend. Ein Benutzer könnte keinen Mittelnamen haben? Machen Sie ihn nullable. Eine Bestellung könnte keinen Rabattcode haben? Nullable. Ein Produkt könnte kein Gewicht haben? Sie wissen, was ich meine.

Primärschlüssel-TypSpeichergrößeIndex-LeistungBester Anwendungsfall
Auto-Inkrement INT4 BytesAusgezeichnet (sequentiell)Einzelserver-Systeme, hochvolumige Tabellen
Auto-Inkrement BIGINT8 BytesAusgezeichnet (sequentiell)Großangelegte Einzelserver-Systeme
UUID (v4)16 BytesSchlecht (zufällig)Verteilte Systeme, sicherheitssensitive IDs
ULID/UUID (v7)16 BytesGut (zeitlich sortiert)Verteilte Systeme, die Sortierbarkeit benötigen
Composite KeysVariiertFair bis GutNatürliche Beziehungen, Multi-Tenant-Systeme

In einem besonders katastrophalen Projekt entwarf ich ein Bestandsverwaltungssystem, bei dem etwa 60% der Spalten in allen Tabellen nullable waren. Das schien vernünftig—nicht jedes Feld würde immer Daten haben, oder? Warum Standards setzen, wenn NULL eindeutig "kein Wert" kommuniziert?

Die Probleme begannen sofort. Abfragen wurden zu einem Minenfeld von NULL-Prüfungen. Möchten Sie alle Produkte ohne Gewicht finden? Sie würden denken, "WHERE weight IS NULL" würde funktionieren, aber was ist mit Produkten, bei denen das Gewicht ausdrücklich auf null gesetzt wurde? Jetzt brauchen Sie "WHERE weight IS NULL OR weight = 0". Möchten Sie die Gesamtsummen der Bestellungen summieren? Besser COALESCE verwenden oder Ihre SUMMEn könnte NULL zurückgeben, wenn irgendein individueller Wert NULL ist.

🛠 Entdecken Sie unsere Tools

JavaScript Minifier - Komprimieren Sie JS-Code kostenlos → Changelog — txt1.ai → CSS Minifier - Komprimieren Sie CSS online kostenlos →

B

T

Written by the Txt1.ai Team

Our editorial team specializes in writing, grammar, and language technology. We research, test, and write in-depth guides to help you work smarter with the right tools.

Share This Article

Twitter LinkedIn Reddit HN

Related Tools

Code Diff Checker - Compare Two Files Side by Side Free Use Cases - TXT1 HTML to PDF Converter — Free, Accurate Rendering

Related Articles

AI Coding Tools in 2026: An Honest Assessment — txt1.ai TypeScript vs JavaScript in 2026: Which Should You Learn? — txt1.ai Clean Code: 10 Principles That Make You a Better Developer — txt1.ai

Put this into practice

Try Our Free Tools →

🔧 Explore More Tools

Essay OutlinerSitemapMerge Pdf Vs Split PdfAi Database DesignerHtml To MarkdownMarkdown To Html

📬 Stay Updated

Get notified about new tools and features. No spam.