21 Punkte von GN⁺ 2024-07-06 | 4 Kommentare | Auf WhatsApp teilen
  • UUIDs werden häufig als Primärschlüssel in Datenbanktabellen verwendet
    • Sie lassen sich leicht erzeugen, einfach zwischen verteilten Systemen teilen und garantieren Eindeutigkeit
    • Angesichts der Größe von UUIDs fragt man sich zwar, ob das die richtige Wahl ist, aber oft kann man das nicht selbst entscheiden
  • Dieser Artikel konzentriert sich nicht auf die Frage „Sind UUIDs ein geeignetes Format für Schlüssel?“, sondern erklärt, wie man UUIDs in PostgreSQL effizient als Primärschlüssel verwendet

PostgreSQL und UUIDs als Primärschlüssel verwenden

  • Was ist eine UUID?
    • UUIDs werden häufig als Primärschlüssel in Datenbanktabellen verwendet
    • Sie lassen sich leicht zwischen verteilten Systemen teilen und garantieren Eindeutigkeit
    • Wegen ihrer Größe kann man an ihrer Eignung zweifeln, aber oft gibt es keine echte Wahl

UUID-Datentyp in PostgreSQL

  • UUIDs als Zeichenkette speichern

    • PostgreSQL bietet zum Speichern von Zeichenketten den Datentyp text
    • Der text-Typ ist jedoch nicht gut geeignet, um UUIDs zu speichern
    • PostgreSQL bietet mit uuid einen eigenen Datentyp für UUIDs
    • Der uuid-Typ ist ein 128-Bit-Datentyp und benötigt 16 Byte pro Wert
    • Beim text-Typ kommen 1 oder 4 Byte Overhead hinzu
  • Experimentelle Ergebnisse

    • Zum Vergleich wurden zwei Tabellen erstellt: eine mit text, die andere mit uuid
    • Nach dem Einfügen von 10.000.000 Zeilen wurden Tabellen- und Indexgröße verglichen
    • Die Tabelle mit text war 54 % größer, der Index sogar 85 % größer

UUIDs und B-Tree-Indizes

  • B-Tree-Indizes und UUIDs

    • Zufällige UUIDs eignen sich nicht gut für B-Tree-Indizes
    • B-Tree-Indizes arbeiten gut mit geordneten Werten
    • UUID.randomUUID() in Java gibt eine UUID v4 zurück, also einen pseudozufälligen Wert
    • UUID v7 erzeugt zeitlich sortierte Werte und ist daher besser für B-Tree-Indizes geeignet
  • Verwendung von UUID v7

    • Um UUID v7 in Java zu verwenden, wird die Bibliothek java-uuid-generator benötigt
    • Das Erzeugen von UUID v7 kann die Insert-Performance verbessern

Einfluss von UUID v7 auf die INSERT-Performance

  • Experiment
    • Es wurde eine Tabelle mit UUID v7 erstellt, und zur Messung der Performance wurden 10-mal jeweils 10.000 Zeilen eingefügt
    • Die Ergebnisse sind etwas zufällig, aber das Einfügen von UUID v7 ist etwa doppelt so schnell

Weiterführende Lektüre

  • In PostgreSQL 17 könnte UUID v7 nativ unterstützt werden
  • Informationen zum UUID-v7-Format
  • Auswirkungen von UUIDs auf die Performance von Primärschlüsseln in Datenbanken

Zusammenfassung

  • Problem der UUID-Länge

    • Selbst mit Optimierungen sind UUIDs nicht der optimale Typ für Primärschlüssel
    • Wenn man die Wahl hat, sollte man andere Optionen wie TSID in Betracht ziehen
  • Notwendigkeit von Optimierungen

    • Bei großen Datensätzen oder erwartet hohem Traffic sollte man Optimierungen in Betracht ziehen
    • Einen Primärschlüssel später zu ändern ist schwierig, deshalb ist es wichtig, ihn von Anfang an richtig zu wählen
  • Hinweise

    • Der Autor ist kein PostgreSQL-Experte, sondern teilt lediglich, was er gelernt hat
    • Wer den Artikel nützlich fand, möge gern per Kommentar oder auf Twitter Feedback geben

Zusammenfassung von GN⁺

  • Dieser Artikel behandelt effiziente Methoden, UUIDs in PostgreSQL als Primärschlüssel zu verwenden
  • Experimente zeigen, dass UUID v7 die Insert-Performance verbessern kann
  • Bei großen Datensätzen oder erwartet hohem Traffic sind Optimierungen wichtig
  • Auch andere Optionen wie TSID sind einen Blick wert

4 Kommentare

 
savvykang 2024-07-09

Ist es zu viel verlangt, sich für uuid statt des Standardformats (Hexadezimal + Bindestriche) eine Base62-Kodierung zu wünschen?

 
qurare 2024-07-08

uuidv7 ist unbesiegbar

uuidv8+ ist ein „Gott“

 
bbulbum 2024-07-08

Die größte Hürde ist, dass sie nicht menschenfreundlich sind … In vielen Bereichen brauche ich das immer noch.

 
GN⁺ 2024-07-06
Hacker-News-Kommentare
  • Es wird empfohlen, bigserial als B-Tree-freundlichen Primärschlüssel zu verwenden und UUIDs, als String kodiert, als Option für externe Record-Locator in Betracht zu ziehen

    • Wenn nichttechnische Nutzer sie zitieren sollen, sollte man zuerst einfache Optionen wie Locator im PNR-Stil erwägen
    • Innerhalb des Schemas eines Service oder einer Anwendung keine PK-Typen mischen
    • UUIDv7 als eindeutigen Identifikator nur für Daten verwenden, in denen ein Timecode inhärent ist
    • Keine hashids verwenden; sie haben keine kryptografische Qualität und sind für normale Menschen nicht vertraut
    • Bei der Kodierung kein base64 oder Alphabete mit Bindestrichen verwenden
  • Beim Entwurf eines Datenbankschemas sollte man die Prinzipien der Trennung von Zuständigkeiten und der mechanischen Sympathie im Blick behalten

  • Die typisierten zufälligen IDs von Stripe sind in Wirklichkeit nicht zufällig

    • Sie enthalten Metadaten, eingebettete Zeitstempel, Shards und Referenzschlüssel, Versionsinformationen usw.
    • Persönlich wird ein mit base58 kodierter AES-verschlüsselter bigserial+HMAC-Locator bevorzugt
  • In Postgres sind zufällige UUIDs kein großes Problem

    • UUIDs (16 Byte) sind zwar größer als serial (4 Byte) oder bigserial (8 Byte), aber auf Ebene der gesamten Tabelle ist das kein großes Problem
  • Bevor man in Postgres über serial vs. zufällige UUID vs. geordnete UUID nachdenkt, gibt es viele andere Dinge, über die man sich Sorgen machen sollte

  • Kürzlich wurde ULID als Postgres-PK gewählt, und dieser Artikel war dabei sehr hilfreich: https://brandur.org/nanoglyphs/026-ids

  • ULID wird bevorzugt, weil es mit dem UUID-Typ kompatibel ist und einen eingebauten Zeitstempel hat, sodass eine Sortierung nach ID zugleich eine Sortierung nach Zeitstempel ist

  • Es wäre gut, auch int64 in den Vergleich aufzunehmen, um den Overhead von UUIDs gegenüber dem traditionellen Ansatz zu vergleichen

  • Einfügeleistung ist eine schlechte Methode zur Leistungsbewertung

    • Die B-Tree-Leistung ist beim Einfügen besser, aber es ist fraglich, wie es bei großen Transaktionen aussieht
  • In SQLite wird UUID4 bevorzugt, weil die Wahrscheinlichkeit von Page-Cache-Kollisionen während Transaction-Locks geringer ist

    • Das könnte auch auf Postgres-Systeme in ähnlicher Weise zutreffen
  • Ganzzahlige autoinkrementierende Primärschlüssel werden bevorzugt

    • Sie sind leicht zu verstehen und einfach zu sortieren
    • In großen Batch-Projekten kann man den letzten Primärschlüssel speichern und alles abrufen, was größer ist
  • Das Benchmarking der Einfügezeit für UUIDv7 schließt die Zeit zur UUID-Erzeugung ein

    • Man würde gern ausschließlich die Kosten der Indexaktualisierung isoliert sehen
  • Es ist unwahrscheinlich, dass PostgreSQL 17 Unterstützung für UUIDv7 enthalten wird

    • Beim jüngsten Stand der Arbeiten wurde der Committer entfernt, und Version 17 befindet sich bereits im Feature-Freeze
  • Es wurde begonnen, python-ulid zu verwenden, und ULID ist UUID überlegen

  • Da der Link zum UUID-v7-Standard veraltet ist, sollte RFC 9562 referenziert werden: https://datatracker.ietf.org/doc/html/rfc9562