UUIDs in Datenbanken

UUIDs (Universally Unique Identifiers) ​​sind 128-Bit-Werte, die u.a. in Datenbanken dazu verwendet werden, um Tabelleneinträge eindeutig zu identifizieren. Sie werden als hexadezimale Zeichenfolge, die in fünf durch Bindestriche getrennte Gruppen unterteilt ist, dargestellt (Beispiel: 09fe49b3-4d2b-471c-ac04-36c9e706b85f). Es gibt zahlreiche Diskussionen über die Vor- und Nachteile von UUIDs in Datenbanken – in verteilten Systemen sind sie unumgänglich.


In Microservices und Multi-Tenancy-Anwendungen lohnt es sich daher, eine Einführung von UUIDs in Betracht zu ziehen. Eine Umstellung vom Datentyp BigInteger auf UUID (eine sortierbare Variante von UUID-Version 4 aus der RFC 4122-Spezifikation) im PHP-Framework Laravel ist schnell erledigt: Zunächst erstellen wir einen neuen Trait:

2aa7136d977617159be1834eaf40e871

Anschließend ergänzen wir alle unsere Models:

2aa7136d977617159be1834eaf40e871

In Laravel 9⁺ ist dies sogar noch einfacher: Hier gibt es bereits den einsatzbereiten Trait HasUuids. Alternativ bietet das Framework auch Support für den verwandten, aber noch eher unbekannten Datentyp ULID, der durch bessere Lesbarkeit und Sortierbarkeit interessant sein könnte.

Der schwierige Part ist die Migration von bestehenden Daten. Grob könnte man wie folgt vorgehen:

  1. Neue UUID-Spalten mit leeren Werten zu allen Tabellen hinzufügen
    (jeweils auf Basis aller Primär- und Fremdschlüsselspalten)
  2. UUID-Werte in die neuen Spalten schreiben
    (mit aufsteigenden Primärschlüssel sowie aktualisierten Fremdschlüsseln)
  3. Ursprüngliche Spalten löschen
  4. Neuen Spalten umbenennen

Hier gibt es mehrere Herausforderungen: Der Migrationsprozess dauert sehr lange und neue Spalten fügen sich ans Ende der Tabelle an (mögliche Lösung: Spalten umsortieren). Ein deutlich direkterer Weg besteht darin, die Spalten direkt zu transformieren.

Setzt man das PostgreSQL ein, kann man dazu (natürlich nach vorherigem Backup) beispielsweise folgendes Query ausführen (vorher kann man alle Tabellen, die man exkludieren in Z. 19/31 ersetzen, sowie eigene spezielle Regeln in Z. 37/39 hinzufügen) und kopiert sich alle daraus erzeugten Queries:

2aa7136d977617159be1834eaf40e871

Wenn man die erzeugten Queries nun gesammelt ausführt, hat man innerhalb kurzer Zeit die Datenbank migriert. Die so erzeugten UUIDs entsprechen zwar nicht der v4-Spezifikation, aber sie sind lexigrafisch in derselben Reihenfolge wie die vorherigen Einträge, kollidieren nicht mit neuen UUIDs (in der 3. Gruppe ist in v4 immer eine 4, in der migrierten Variante immer eine 0), was auch bedeutet, dass neue UUIDs stets größer als die migrierten UUIDs sind.

Danach empfiehlt es sich, alle Laravel Caches (php artisan cache:clear && php artisan route:clear && php artisan config:clear && php artisan view:clear && composer dump-autoload && rm -rf bootstrap/cache/*/*) sowie laufende Sessions (rm -f storage/framework/sessions/*) zu leeren. Das Ganze kann man natürlich auch innerhalb einer Laravel-Migration realisieren:

2aa7136d977617159be1834eaf40e871

Nach der Umstellung verwendet man in zukünftigen Migrationen statt bigIncrements bzw. bigInteger dann uuid:

2aa7136d977617159be1834eaf40e871

Zurück