UUIDv7-Zeitstempel sind nicht das, was du denkst
UUIDv7 wird meist mit drei Versprechen verkauft: IDs, die immer steigen, ein Zeitstempel, der dir sagt, wann die Zeile angelegt wurde, und IDs, die niemand erraten kann. In einem Beitrag vom 19. Oktober 2026 argumentiert Robert Alexander, dass nur eine Eigenschaft wirklich hält, die Eindeutigkeit, und dass die Spezifikation selbst den Rest biegen lässt. Wenn du in Laravel HasUuids nutzt, nutzt du UUIDv7. Es lohnt sich also zu wissen, auf welches dieser Versprechen dein Code baut.
Die Spezifikation lässt den Zeitstempel wandern
RFC 9562 sagt, Implementierungen dürfen den tatsächlichen Zeitstempel ändern, garantiert nichts darüber, wie nah der Uhrwert an der echten Zeit liegt, und erlaubt, ihn über die tatsächliche Zeit hinaus hochzuzählen. Postgres schreibt in der Doku zu uuid_extract_timestamp dasselbe: Der ausgelesene Wert ist nicht unbedingt der Moment, in dem die UUID erzeugt wurde.
Der Grund ist ein Zielkonflikt zwischen sortierten IDs und genauer Zeit. Geht die Uhr zurück, muss ein Generator, der nicht rückwärts gehen will, mit einem Zeitstempel weitermachen, der jetzt in der Zukunft liegt.
Python zeigt, wie weit das gehen kann
Alexanders Beispiel nutzt uuid.uuid7() aus Python 3.14. Er stellt die Uhr eine Stunde zurück, während das Skript in einer Schleife IDs erzeugt. Erwarten würde man einen Abstand von 3.600 Sekunden zwischen eingebettetem Zeitstempel und Uhr, der dann schrumpft. Bei mehr als einer ID pro Millisekunde wächst er stattdessen: Sein Lauf gab etwa 3.695 aus, dann 3.795, 3.894 und 3.994 Sekunden. Das zugehörige Python-Issue hält das für vereinbar mit dem RFC und mit anderen Implementierungen, und er schreibt, dass er dasselbe Verhalten auch anderswo sieht.
Was der Generator von Laravel macht
Ich habe den Code im vendor/-Ordner dieser Seite gelesen, statt zu raten. HasUuids erzeugt Schlüssel mit Str::uuid7(), das ramsey/uuid aufruft. Dessen UnixTimeGenerator nimmt die Zeit in Millisekunden und merkt sich die zuletzt genutzte Zeit in einer statischen Eigenschaft. Ist die neue Zeit später, wählt er frische Zufallsbits. Wenn nicht, also gleiche Millisekunde oder zurückgestellte Uhr, behält er die letzte Zeit und zählt den Zufallsteil hoch. Läuft dieses Hochzählen über, schiebt er die gespeicherte Zeit eine Millisekunde nach vorn.
Praktisch heißt das, allein aus dem Code: Innerhalb eines PHP-Prozesses geht der Zeitstempel nie zurück, und nach einem Uhrsprung zurück tragen neue IDs die ältere, später wirkende Zeit, bis die Uhr aufholt. Die letzte Zeit liegt im Speicher, nicht auf der Platte, also startet ein neuer Prozess frisch, und zwei Server wissen nichts von der Uhr des anderen. Nichts darin verspricht eine Reihenfolge über Prozesse oder Maschinen hinweg.
Zwei kleinere Vorbehalte
Der Zeitstempel ist sichtbar. Du kannst keine UUIDv7 weitergeben, ohne auf die Millisekunde mitzuteilen, wann sie entstand. Alexander stuft das für die meisten Apps als gering ein, aber eine Profil-URL, die den Registrierungszeitpunkt des Kontos verrät, ist ein Fall, in dem es zählt.
Die Reihenfolge ist grob. Uhren auf verschiedenen Maschinen lassen sich nicht auf die Millisekunde synchronisieren, und eine Implementierung ohne Zähler kann schon in einer einzigen Schleife IDs außer der Reihe liefern. Grobe Ordnung hilft trotzdem der Lokalität im Index. Eine Sequenz ist sie nicht.
Worauf du dich in einer Laravel-App verlassen kannst
Verlass dich auf die Eindeutigkeit und darauf, dass die grobe Einfügereihenfolge dem Index guttut. Lies nicht aus der ID heraus, wann eine Zeile angelegt wurde: Behalte created_at und sortiere danach, wenn die Reihenfolge für einen Menschen zählt. Nimm die ID nicht als Beweis, welches Ereignis über Server hinweg zuerst kam. Und wenn der Erstellungszeitpunkt privat ist, gehört keine UUIDv7 in eine öffentliche URL. Wenn du dir den Aufbau einer solchen ID ansehen willst, erzeugt der UUID-Generator auf dieser Seite v7-IDs direkt in deinem Browser.
created_at als Nachweis für das Wann.Nächster Schritt: Durchsuch deinen Code nach Stellen, die die Zeit aus einer UUID lesen oder nach der ID sortieren, wo ein Mensch eine zeitliche Reihenfolge erwartet, und stell sie auf created_at um. Quelle: Robert Alexander, UUIDv7 doesn't have the properties you think it does.
Notizen der Leser