Heute geht’s um eine dieser unscheinbaren Technologien, die du wahrscheinlich täglich nutzt: UUIDs! Ob in deiner Datenbank, im Betriebssystem oder in verteilten Systemen. Wie und warum funktionieren UUIDs, welche Versionen gibt es und warum ist nicht jede UUID gleich gut für deine Datenbank.. Außerdem: Alternativen wie Snowflake, ULID oder NanoID..
Im Engineering-Kiosk-Adventskalender 2025 sprechen befreundete Podcaster⋅innen und wir selbst, Andy und Wolfi, jeden Tag kurz & knackig innerhalb weniger Minuten über ein interessantes Tech-Thema.
Unsere aktuellen Werbepartner findest du auf https://engineeringkiosk.dev/partners
Das schnelle Feedback zur Episode:
Anregungen, Gedanken, Themen und Wünsche
Dein Feedback zählt! Erreiche uns über einen der folgenden Kanäle …
- EngKiosk Community: https://engineeringkiosk.dev/join-discord
- LinkedIn: https://www.linkedin.com/company/engineering-kiosk/
- Email: stehtisch@engineeringkiosk.dev
- Mastodon: https://podcasts.social/@engkiosk
- Bluesky: https://bsky.app/profile/engineeringkiosk.bsky.social
- Instagram: https://www.instagram.com/engineeringkiosk/
Unterstütze den Engineering Kiosk
Wenn du uns etwas Gutes tun möchtest … Kaffee schmeckt uns immer
- Buy us a coffee: https://engineeringkiosk.dev/kaffee
Links
- The Problem with Using a UUID Primary Key in MySQL https://planetscale.com/blog/the-problem-with-using-a-uuid-primary-key-in-mysql
- Storing UUID Values in MySQL Tables https://dev.mysql.com/blog-archive/storing-uuid-values-in-mysql-tables/
- RFC: Universally Unique IDentifiers (UUIDs) https://datatracker.ietf.org/doc/html/rfc9562
- Always use the right UUID in MariaDB https://mariadb.org/always-use-the-right-uuid/
- UUIDs https://en.wikipedia.org/wiki/Universally_unique_identifier
Sprungmarken
Hosts
- Wolfgang Gassler (https://gassler.dev)
- Andy Grunwald (https://andygrunwald.com/)
Community
Diskutiere mit uns und vielen anderen Tech-Spezialist⋅innen in unserer Engineering Kiosk Community unter https://engineeringkiosk.dev/join-discord
Transkript
Andy Grunwald (00:00:06 - 00:00:14)
Du bist hier im Engineering Kiosk gelandet und zwar im Engineering Kiosk Adventskalender. Leider hat dieser Kalender nichts mit Schokolade zu tun.
Andy Grunwald (00:00:16 - 00:00:31)
Na gut, machst du nix. Als Alternative bieten wir ein wenig Wissen an. Ein Thema kurz und knapp in ein paar Minuten und das Ganze bis Heiligabend. Präsentiert von Knecht, Ruprecht und Krampus. Ach, scheiße falsche Zeile. Präsentiert von Wolfi. Los geht's.
Wolfi Gassler (00:00:42 - 00:17:05)
Acht tausend sieben hundert drei und fünfzig vier tausend ein hundert fünf und fünfzig BC vier ein und vierzig ein und vierzig sieben und zwanzig e zwei tausend vier sieben Kennt ihr wahrscheinlich alle oder sind die sogenannten uuids. Findet man eigentlich überall. Kennt man vielleicht aus dem FSDAP File im Linux Filesystem. Podcast Episoden waren so auch eindeutig definiert. Also sechs und dreiig Stellen sind ein hundert acht und zwanzig bit, weil es zwei und dreiig Hex Character sind und eben vier Bindestriche dazwischen. Diese uuids sind wirklich sehr beliebt und stammen ursprünglich eigentlich schon aus den er Jahren, dem Apollo Computer, der besonders für den Netzwerkbereich diese uuids verwendet hat. Später ist es auch aufgegriffen worden von der OSF oder wer vielleicht aus der Microsoft Welt kommt, kennt diese guids oder GUID oder im Englischen auch teilweise JIT genannt, sind Global Unique Identifiers. Microsoft packt da gern so geschwungene Klammern drumherum, um so eine UUID oder GUID zu definieren, wo viele jetzt vielleicht einfach nur einen random String sehen. Das Ganze ist auch wirklich standardisiert, gibt auch mehrere Versionen von uuids. Aber die Frage, die sich eigentlich stellt, warum verwendet man diese uuids Und die Idee ist, dass uuids wirklich eindeutig sind, nennen sich ja auch Universally Unique Identifiers, dass die unique sind weltweit, auch wenn sie nicht am selben System erzeugt worden sind. Also im Gegensatz zu Auto Increment in Datenbanken, wo man einfach hochzählt, wenn es zwei Datenbanken machen auf zwei verschiedenen Rechnern, bekommt man natürlich Überlappungen und dieselben ids. Mit uuids passiert das eigentlich nicht. Wie funktioniert es nun, dass es da keine Überschneidungen gibt oder dass das Ganze eindeutig bleibt? Das hat einfach damit zu tun, dass das ein sehr großer Zahlenraum ist. Also wir sprechen da von zwei hoch ein hundert acht und zwanzig Möglichkeiten. Also das ist eine Zahl mit acht und dreiig Nullen Und wenn man jedes Sandkorn auf allen Stränden auf der Welt durchnummerieren würde, hätte man trotzdem noch neun und neunzig komma neun Prozent von dem gesamten Zahlenraum frei. Und dadurch der Zahlenraum so groß ist, passieren Kollisionen eigentlich in der Realität nicht. Und das, obwohl nicht mal die sechs und dreiig Stellen alle für die Zahl an sich genutzt werden. Es gibt auch eine Version, die noch codiert ist und eine Variante im vierten Block, die zusätzlich noch in der UUID automatisch mitkodiert wird. Und genau diese Kollisionsfreiheit ist eigentlich das Interessante an uuids und warum sie auch so gerne verwendet werden, Weil man muss nur dran denken an verteilte Systeme, Microservices, die jetzt immer stärker aufgekommen sind. Wenn man Offline Systeme zum Beispiel hat oder auch Edge Systeme, die möglichst schnell antworten müssen, möglichst schnell ids erzeugen müssen, dann verwendet man natürlich gerne uuids, weil jedes System das selbstständig erzeugen kann, ohne mit einer zentralen Autorität sprechen zu müssen. Und wenn man dann natürlich Daten zusammenführt später, ist es essentiell, dass man da unique ids hat und keine Kollisionen bei einem Merge dann auftauchen. Zusätzlich, was für einen Vorteil man noch kostenlos sozusagen mitbekommt, ist, dass die ids nicht erraten werden können. Das heißt, gerade bei einer öffentlichen API zum Beispiel, wenn man dann Auto Increment hat und User eins, User zwei, User drei kann natürlich ein Mensch sehr schnell erraten, okay, die nächste ID ist User vier, User fünf, User sechs und kann dann vielleicht auch gewisse Dinge ausprobieren. Wenn man so eine UUID hat, ist es eigentlich kaum möglich, die nächste ID zu erraten und dadurch sind natürlich solche Brute Force Attacken unter Umständen auch wesentlich schwerer durchzuführen. Jetzt werden natürlich vielleicht viele sagen, ja, aber verteilte Systeme haben ja meistens auch Verantwortlichkeiten und jedes verteilte System ist für eine Art von Entität verantwortlich. Aber in der Realität muss man schon auch sagen, dass das immer schwieriger wird, vor allem wenn wir in größeren Firmen verteilte Teams verteilte Systeme haben. Und da kann es dann auch natürlich solche Shared Responsibilities geben, wenn man zum Beispiel sich überlegt, man hat so ein Hotelmanagement System, wo man neue Hotels anlegen kann und man will jetzt diese Hotels nummerieren und eine ID vergeben. Jetzt können natürlich neue Hotels reinkommen bei irgendwelchen automatischen Imports von irgendwelchen Partnern, von Schnittstellen. Es kann der Support vielleicht ein neues Hotel anlegen in einem Online Tool, der User kann vielleicht selbstständig ein neues Hotel anlegen von einer Hotelkette. Also da sieht man schon, dass dieselbe Entität in verschiedenen Systemen angelegt werden kann. Und wenn man dazu dann keine uuids verwendet, dann wäre die Alternative, dass man eine Single Authority hat, also eine zentrale Stelle, wo man immer anfragen muss, gib mir die nächste verfügbare ID. Da müsste man dann Transaktionen haben, müsste es sauber alles durchführen, müsste Handshakes womöglich haben. Das ganze offline funktioniert natürlich nicht, wenn ich jetzt am Handy irgendwas erstelle, bei meinem Kunden zum Beispiel, bin in dem Hotel vor Ort, habe dort kein Internet aus irgendeinem Grund, will da etwas erstellen, da muss ich später erst wieder eine ID erzeugen, kann das nicht on the fly machen. Also da sind uuids wesentlich flexibler und darum werden sie auch sehr gerne eingesetzt. Ich habe schon erwähnt, es gibt verschiedene Versionen von uuids, also es gibt Version eins bis Version achte Die Version eins und zwei sind zeitbasierte Formate, also da ist die Zeit, wann die ID erstellt wurde und die Mac Adresse auch noch mit rein codiert. Aus dem Grund wird es eigentlich auch nicht mehr empfohlen zu machen, weil wenn man die Mac Adresse irgendwo rein kodiert, ist es sicherheitstechnisch meistens weniger schlau. Version drei fünf sind namensbasierte Varianten, das heißt man hat da eigentlich einen Namespace und einen eigentlichen Namen, einen eigentlichen Wert und und der ganze wird dann gehashed, ganz klassisch mit MD oder SHA und es kommt dann am Ende wieder eine UUID heraus, Da ist der Namespace auch schon codiert, das heißt ich kann da sagen, okay, diese UUID soll ein Hotel sein, diese UUID soll ein Hotelzimmer sein, diese UUID soll ein Preis sein für eine gewisse Zeit, also da kann ich dann über den Namespace solche Dinge schon rein codieren. Unter Namespace selbst ist dann auch wieder eine UUID und dadurch, dass gehashed wird, habe da keine Längenprobleme und kann das immer wieder auf eine UUID zurückführen. Dadurch, dass ein Hash ist, kommt da immer auch dieselbe UUID für dieselben Ausgangswerte heraus und es bleibt also auch so nachvollziehbar. Die wohl bekannteste Version ist die Version vier auch random genannt, die ist also wirklich komplett zufällig. Das ist eigentlich die, die gerne eingesetzt wird bei verteilten Systemen, weil sie halt sehr einfach ist, man kodiert gar nichts hinein, es ist komplett random und man bekommt einfach eine unique ID zurück, die im Normalfall keine Kollision hat. Version sechs hat dann wieder Zeitstempel hineingenommen, auch in Bezug auf Sortierbarkeit, dass man das nach Zeit sortieren kann. Und im Gegensatz zu der Version eins und zwei, die ja auch die Mac Adresse mit dabei haben, hat die Version sechs jetzt keine Mac Adressen mit dabei. Version sieben hat auch die Timestamp, die Unix Timestamp in dem Fall mit dabei und und ist dadurch auch sortierbar und gerade gut für Datenbanken verwendbar. Version acht ist komplett custom, das heißt, da kann man wirklich jedes Format rein speichern und sich selber ein Pattern überlegen, wie man das Ganze aufteilt, den Adressraum. Was natürlich alle gemeinsam haben, ist die Kollisionswahrscheinlichkeit und die ist wirklich sehr niedrig, habe ich auch schon kurz ausgeführt. Aber um das nochmal in Zahlen zu fassen, Es gibt ja dieses bekannte Geburtstagsparadoxon, das besagt, dass wenn man zum Beispiel auf einer Geburtstagsparty ist und man in einem Raum mit drei und zwanzig Personen ist, dann ist die Wahrscheinlichkeit, dass es zwei Personen gibt, die am selben Tag Geburtstag haben, bei fünfzig Prozent. Also man nimmt immer so eine fünfzig Prozent Wahrscheinlichkeit an, dass man eine Kollision hat, also zwei Geburtstage in dem Fall und bei einer Geburtstagsparty wären das schon drei und zwanzig Personen. Bei uuids ist das Ganze zwei hoch vier und sechzig das sind also ungefähr achtzehn Trillionen, dass man eine fünfzig prozentige Wahrscheinlichkeit hat, eine Kollision zu haben. Auch wieder als kleiner Wenn jeder Mensch auf der Erde jede Sekunde eine UUID erstellen würde, dann bräuchten wir Jahrzehnte, bis man eine einzige Kollision hätte in dem ganzen System. Also wenn du dir jetzt ernsthaft irgendwie Gedanken machst, ob ich die uuids verwenden kann, weil da vielleicht Kollisionen auftreten können, ja, dann arbeitest du wahrscheinlich beim CERN oder hast einfach einen extrem schlechten Random Number Generator, weil das ist auch oft ein Problem, dass dann UUID Kollisionen vorkommen. Aber wenn man eine Library verwendet, dann ist es normalerweise sichergestellt und die verwenden dann auch eine saubere Implementierung. Aber in der Informatik gibt es ja nichts ohne Nachteile, sonst wäre es ja schön Die uuids lösen alle unsere Probleme in verteilten Systemen. Aber gerade in Datenbanken, wenn man mit uuids arbeitet, hat man natürlich gewisse Nachteile. Also grundsätzlich hat man mal den Speicherbedarf, der wesentlich höher ist. Also bei einem klassischen Integer sprechen wir normalerweise von vier Byte, ein Bigint acht Byte und eine UUID aber schon sechzehn Byte, wenn sie binär gespeichert wird. Wenn man sie jetzt wirklich als Character speichert, also als Hex Characters, dann hat man überhaupt sechs und dreiig Characters. Das sollte man auf keinen Fall machen, weil auch String Indizes in Datenbanken üblicherweise ganz anders funktionieren und im Normalfall auch wesentlich langsamer sind als binäre Indizes, die wirklich auf den Nummern, auf den Binärdaten arbeiten. Was heißt das jetzt? Sechzehn Byte statt vier Byte oder acht Byte? Also wenn man jetzt eine Milliarde Einträge hätte und das ist ja ungefähr, was ein Integer Platz hat, dann hätte man zwölf Byte Overhead zu vier Byte Integer und es wären am Ende dann schon zehn Gigabyte. Wobei man natürlich da sagen muss, wer hat schon eine Milliarde Rows, die man da so einträgt in der Datenbank. Wenn wir jetzt von einem realistischeren Beispiel ausgehen, wir haben eine Million Einträge, wenn es jetzt um irgendwelche Artikel geht oder Käufe, da sind wir mit einer Million eh schon sehr hoch und da sprechen wir von einem Overhead von zehn Megabyte. Also zehn Megabyte sind heutzutage wirklich kein Problem. Auch zehn Gigabyte sind wahrscheinlich kein Problem, wenn man Milliarden von Einträge hat, weil dann hat man höchstwahrscheinlich sehr große Datenbank, weil die anderen Werte wesentlich mehr Speicher benötigen. Also es fällt jetzt nicht so stark ins Gewicht, solange man auf Binary sechzehn bleibt, also wirklich dem RAW Format von uuids. Ein anderes Problem mit Datenbanken und uuids ist aber der Index an sich und die Sortierung. Jetzt haben wir schon darüber gesprochen, dass die Version vier von uuids komplett random ist, zufällige Werte. Das Problem in Datenbanken und Indexstrukturen und auch die Speicherung der Daten an sich ist, dass das Ganze über B Bäume läuft. Bäume sind ja super optimiert und lieben es aber sequenzielle Inserts zu haben, das heißt, die lieben es sequenzielle ids zu haben, damit die im Baum immer optimiert am Ende in den Blättern hängen. Hat man jetzt uuids, landen diese random Werte natürlich irgendwo im Baum. Das heißt, der Baum kann sich nicht schön langsam füllen, sondern es gibt ganz viele Splits, es werden Blätter gesplittet, es gibt halbleere Knoten. Das Speichermanagement wird viel schwieriger, weil die Pages natürlich ständig aus dem Cash rausgekickt werden, weil Man springt ja im Baum wahllos herum und dafür ist ein B Baum einfach nicht geeignet und auch nicht gemacht worden. Das ist jetzt, wenn man ein tausend Entries hat oder auch vielleicht eine Million Entries, wahrscheinlich noch gar nicht so relevant. Aber man spürt es natürlich schon, wenn man viele Joins hat. Die ids sind ja doch immer ein zentraler Punkt, wo ganz viele Joins gemacht werden, wo Daten gesucht werden. Und da hat man natürlich dann schon das Problem, dass das Ganze langsamer wird. Die Alternative wäre, dass man zeitbasierte Versionen nimmt von den uuids, also Version eins oder besser noch sechs oder sieben weil die Mac Adresse eben nicht mitkodiert ist. Und Postgres und mariadb haben auch eigene UUID Typen, also mariadb erst seit Version zehnter juli Und diese UUID Typen optimieren das Ganze noch mal mehr, dass gewisse Teile nach vorne verschoben werden von den uuids, damit das Ganze noch sequenzieller ist und noch optimierter eingefügt werden kann. Also die machen ein internes Swapping von Teilbereichen einer UUID, um insgesamt noch mal mehr Optimierung rausholen zu können. Also wenn man mit Datenbank arbeitet, sollte man wirklich auf die nativen Typen gehen und eben auf zeitbasierte Formate wie die Version sechs und siebte Es gibt aber auch natürlich Alternativen, weil wie gesagt, die uuids oder jits guids sind schon sehr alt, sind aus den ER Jahren bzw. Die Konstrukte davon sind noch älter und seitdem hat sich natürlich viel getan. Das heißt, es gibt auch mittlerweile Alternativen. Es gibt die Snowflake ids, die ursprünglich Twitter verwendet hat, also das Ganze ist auch wieder Timestamp passiert, habe eine Timestamp, habe dann noch eine Schad ID und einen Sequenz Counter und erreiche somit dann auch die Eindeutigkeit. Es gibt die ulits, die Universally Unique Lexico Graphically Sortable Identifiers, schwieriges Wort, auch wieder Zeitinformation plus ein Zufallswert dahinter. Was mittlerweile auch sehr beliebt ist, sind diese Nano ids, Das sind eigentlich Charakter basierte ids, also die haben ein Alphabet von vier und sechzig Zeichen ohne Sonderzeichen und man verwendet dann zum Beispiel ein und zwanzig Zeichen und das gibt dann wieder eine Entropie von ein hundert acht und zwanzig Bit ungefähr. Kennt man ja, wenn man mehr Zeichen verwendet, dann hat man einfach einen wesentlich größeren Raum mit weniger Zeichen und bekommt dann da auch sehr viel unter. Also noch mal abschließend vielleicht so als wenn ihr in irgendeinem Monolithen unterwegs seid, eure mysql Datenbank habt, irgendeinen Monolithen in PHP oder sonst einer Sprache, kann man Auto Inkrement verwenden, außer man hat jetzt vielleicht das Problem, dass die ids offensichtlich sind und man diese erraten kann und vielleicht auch im Alltag sich dann so einschleicht, dass man dann immer von der ID acht redet, wenn es um irgendeine Kategorie geht und jeder weiß dann, was die ID acht ist, außer der Newcomer, der gerade neu im Team ist. Also auch das ist teilweise gar nicht so gut, wenn man so gewisse Werte so direkt im Kopf hat und immer darüber spricht. Also auch da, wenn man sowas verhindern will, kann man natürlich uuids verwenden, aber sonst ist ein Auto Inkrement vollkommen in Ordnung. Sobald man jetzt in die Verteilung geht und wirklich darauf angewiesen ist, dass man Entitäten bzw. Ihre ids dezentral erstellen will, dann braucht man uuids. Und da ist eigentlich die Empfehlung, weil man muss sie irgendwo wegspeichern. Und üblicherweise sind es Datenbanken, dass man da native UUID Typen verwendet, wenn die Datenbank das hergibt und auf zeitbasierte Versionen geht, also nicht die Version vier die komplett random ist, sondern eben Version sechs und siebte Und auch ganz wichtig, dass man immer mit den Binärwerten rechnet, also ein Binary sechzehn wenn man schon keine nativen UUID Typen verwendet und auf keinen Fall die Character dann wirklich abspeichert, obwohl man natürlich jetzt zum Repräsentieren der ID dann jeweils die Character verwendet, weil die halt human readable sind als die Binärdaten. Und wenn man Probleme mit den uuids hat, kann man eben die diversen Alternativen wie Snowflake ID durchaus mal anschauen. Aber uuids sind natürlich super unterstützt, gibt fast in jeder Sprache irgendwelche Funktionen, um uuids zu erstellen und damit zu arbeiten. Und das macht das Ganze dann natürlich auch wesentlich einfacher im Alltag. Und weder die Kollisionen noch der Speicherbedarf sind im Alltag üblicherweise ein Problem. In den Shownotes findet ihr natürlich noch ein paar Links zu Blog Einträgen, auch von den üblichen Datenbanken Verdächtigen, wo es dann auch mehr um die ganze Implementierung und Optimierung geht. Also falls ihr mal mehr Zeit haben solltet in der ruhigen Adventzeit, sind paar gute Blogartikel dabei und damit wünsche euch noch eine schöne Adventszeit. Bis zur nächsten Episode.
Andy Grunwald (00:17:06 - 00:17:33)
Danke Wolfi für diese Episode. Das war's für heute von uns. Wenn dir das, was du gehört hast, gefallen hat, würden wir uns natürlich immer über Feedback freuen. Entweder über ein positives Review auf der Podcast Plattform deiner Wahl, einen Daumen hoch auf Social Media oder eine Anekdote in unserer Discord Community. Ich bin mir sicher, du findest schon einen Weg. Wir freuen uns auch, wenn du diesen Podcast abonnierst und einfach bei der nächsten Episode wieder einschaltest. Wir hören uns bald. Schöne Weihnachtszeit und tschüss.