63 Punkte von GN⁺ 2025-04-01 | 4 Kommentare | Auf WhatsApp teilen
  • Ein Artikel, der praktische Muster zusammenfasst, mit denen sich Postgres produktiver und sicherer nutzen lässt
  • Jedes einzelne Muster ist klein, aber in der Summe machen sie einen großen Unterschied

UUIDs als Primärschlüssel verwenden

  • UUIDs sind zufällig und haben deshalb Nachteile bei Sortierung und Index-Performance
  • Sie benötigen mehr Speicherplatz als numerische IDs
  • Sie haben jedoch folgende Vorteile
    • UUIDs lassen sich auch ohne Verbindung zur DB erzeugen
    • Sie können sicher nach außen offengelegt werden
  • Mit gen_random_uuid() lassen sich UUIDs automatisch als Primärschlüssel erzeugen

Felder created_at und updated_at immer hinzufügen

  • Beim Debugging ist es sehr nützlich zu wissen, wann ein Datensatz erstellt und geändert wurde
  • updated_at kann so konfiguriert werden, dass es per Trigger automatisch aktualisiert wird
  • Die Funktion muss nur einmal erstellt werden, der Trigger jedoch für jede Tabelle einzeln

Für Fremdschlüssel on update restrict / on delete restrict setzen

  • Beim Definieren von Fremdschlüssel-Constraints sollte unbedingt on update restrict on delete restrict verwendet werden
  • So wird verhindert, dass beim Löschen von Daten versehentlich kaskadierende Löschungen ausgelöst werden
  • Speicherplatz ist billig, aber Datenwiederherstellung ist sehr schwierig, daher ist ein konservativer Umgang sinnvoll

Die Verwendung von Schemas wird empfohlen

  • Das Standard-Schema ist public, aber wenn eine Anwendung wächst, ist es besser, sie in separate Schemas aufzuteilen
  • Schemas funktionieren wie Namespaces, und Joins zwischen unterschiedlichen Schemas sind ebenfalls möglich
  • Je mehr Tabellen es gibt, desto vorteilhafter ist der Einsatz von Schemas für Lesbarkeit und Wartbarkeit

Das Enum-Tabellenmuster verwenden

  • Statt des Enum-Typs von PostgreSQL oder einer Check Constraint ist die Verwendung von Enum-Tabellen flexibler
  • Werden Enum-Werte in einer separaten Tabelle verwaltet, lassen sich Metadaten hinzufügen oder Enum-Werte leicht erweitern
  • Die Werte der Enum-Tabelle werden per Fremdschlüssel referenziert, um die Constraints beizubehalten

Tabellennamen im Singular vergeben

  • Tabellennamen sollten vorzugsweise im Singular statt im Plural vergeben werden
  • Beim Schreiben von Queries ist der Singular klarer, während der Plural zu grammatikalischer oder semantischer Verwirrung führen kann

Join-Tabellen mechanisch benennen

  • Join-Tabellen für Many-to-many-Beziehungen sollten sicher und eindeutig benannt werden, indem die beiden Tabellennamen zusammengesetzt werden
  • Beispiel: person_pet
  • Ergänze einen eindeutigen Index auf die Kombination, um Duplikate zu verhindern

Soft Delete statt echter Löschung verwenden

  • Statt Daten tatsächlich zu löschen, ist es besser, ein Timestamp-Feld wie revoked_at zu verwenden, das den Löschzeitpunkt angibt
  • So lässt sich nicht nur erkennen, ob etwas gelöscht wurde, sondern auch wann
  • Ein Timestamp liefert mehr Informationen als ein Boolean-Wert

Status als Log-Tabelle modellieren

  • Statt den Status in einer einzelnen Spalte auszudrücken, sollte der Verlauf von Statusänderungen in einer separaten Tabelle gespeichert werden
  • Der Zeitpunkt eines Statusereignisses wird explizit in der Spalte valid_at festgehalten
  • Für schnelle Abfragen des aktuellen Status werden ein latest-Flag sowie ein eindeutiger Index und ein Trigger eingerichtet
  • Das ist vorteilhaft bei asynchroner Event-Verarbeitung oder in Situationen, in denen die Reihenfolge durcheinandergeraten kann

Für besondere Zeilen system_id hinzufügen

  • Nicht nur bei Enum-Tabellen, auch sonst werden manchmal bestimmte „System-Zeilen“ benötigt
  • Füge ein nullable Textfeld system_id hinzu und setze einen eindeutigen Index darauf
  • Über system_id lassen sich bestimmte Zeilen eindeutig abfragen

Views nur sparsam einsetzen

  • Views sind nützlich, um komplexe Queries zu abstrahieren, aber sie sind schwer zu warten
    • Wenn Spalten entfernt werden, müssen Views neu erstellt werden
    • Werden Views auf Views aufgebaut, entstehen Probleme bei Performance und Lesbarkeit
  • Deshalb sollten sie nur so weit wie nötig und mit Bedacht eingesetzt werden

JSON-Queries aktiv nutzen

  • Postgres ist nicht nur beim Speichern von JSON stark, sondern auch bei Queries, die JSON zurückgeben
  • Verschachtelte Beziehungen lassen sich mit einer einzigen Query als JSON zurückgeben
  • So können alle benötigten Daten ohne N+1-Problem auf einmal geladen werden
  • Nachteil: Typinformationen gehen verloren, und alle Daten müssen auf einmal in den Speicher geladen werden
  • Die Vorteile bei Performance oder Struktur überwiegen jedoch deutlich

4 Kommentare

 
jhj0517 2025-04-01

> Join-Tabellen mechanisch benennen

Ich finde es an sich gut, dass es überhaupt so eine Regel für die Benennung gibt~

 
halfenif 2025-04-01

Wenn man UUID7 in Betracht zieht, wäre dann keine Sortierung nach Zeit möglich?

 
winterjung 2025-04-01
 
t7vonn 2025-04-01

Die Methode, bei Soft Deletes einen Timestamp zu setzen, ist gut. Wenn man als Primärschlüssel eine UUID verwendet, ist keine Sortierung nach Zeit möglich; daher scheint es ebenfalls sinnvoll zu sein, eine Snowflake-ID oder ULID zu verwenden. In diesem Fall muss allerdings jeder Server eine Sequence-Number vorhalten.