What a UUID actually is
UUID stands for Universally Unique Identifier, and the name is doing real work: it's a 128-bit value designed to be unique across systems that have never talked to each other, without anyone checking a central list first. Written out, it looks like 3fa85f64-5717-4562-b3fc-2c963f66afa6 — 32 hexadecimal digits split into groups of 8-4-4-4-12 by dashes that exist purely for human eyes. Strip the dashes and it's just a 128-bit number.
Two of those bits aren't random at all. The first character of the third group encodes the UUID's version, and the leading bits of the fourth group encode its "variant," which is really just a flag saying "this follows the RFC 4122 layout." So when you see a UUID starting with a 4 in that third group, that's not coincidence — it's the format telling you exactly how this particular identifier was built, which matters more than it sounds like it should, because different versions are built from completely different inputs.
UUID v4, and what the other versions are doing instead
UUID v4 is the version most people mean when they say "just give me a UUID," and it's the one this site's generator produces. Aside from the four fixed version bits and two fixed variant bits, every other bit in a v4 UUID comes from a random number generator. That's 122 bits of actual entropy, which is enormous — enough that collision isn't a realistic concern (more on the exact math below). Nothing about a v4 UUID tells you when it was created or where. It's designed to reveal nothing.
Other versions trade some of that randomness for other properties. UUID v1 packs in a timestamp and the generating machine's MAC address, so two people who know the format can compare v1 UUIDs and learn roughly when each was created and, if they care to dig, which physical machine made it. That's a privacy leak most applications don't want, and it's why v1 fell out of favor outside of a few niche uses. UUID v7 is the newer entrant, standardized in 2024, and it's worth a mention because it fixes the one real weakness of v4: it puts a millisecond timestamp in the leading bits and keeps the rest random, so v7 UUIDs sort roughly in creation order while still being effectively unguessable. That ordering turns out to matter a lot for database performance, which is the next section.
Why anyone bothered inventing this instead of just counting
An auto-increment column works great as long as there's exactly one place doing the incrementing. The moment you have two databases, two offline mobile clients, or a batch import job generating records before they ever touch your server, a simple counter falls apart — two clients can independently pick the number 4,812 for two different orders, and now merging those datasets means rewriting IDs and fixing up every foreign key that pointed at the old ones. A UUID sidesteps the whole problem because any machine, anywhere, with no network connection and no coordination with anyone else, can generate an ID that won't collide with one generated a thousand miles away. That's the entire reason UUIDs exist: they let record creation happen anywhere, not just at one authoritative counter.
There's a second, quieter reason people reach for them: sequential IDs leak information. If your invoice URLs are /invoice/10482, a competitor or a curious customer can watch that number over a few weeks and back out your order volume, or just increment the number in the URL to peek at someone else's invoice (a class of bug called an insecure direct object reference, and it shows up constantly in security audits of exactly this pattern). A random UUID in that URL gives an attacker nothing to guess and gives a competitor no growth curve to read off your ID sequence. That's a real, practical reason to prefer UUIDs for anything customer-facing, independent of the distributed-systems argument.
The tradeoff nobody mentions until it shows up in a slow query
Here's the part that gets skipped in most "just use UUIDs" advice: they cost you something real, and it's not just the extra characters in a URL. A UUID stored efficiently as a binary value takes 16 bytes. A standard auto-increment integer takes 4 or 8. On a table with a few thousand rows that difference is noise. On a table with hundreds of millions of rows, spread across a primary key and every foreign key that references it, it adds up to a genuinely larger database, more RAM needed to keep indexes cached, and slower backups.
The bigger cost is where the value lands in the index, not how big it is. Auto-increment integers arrive in order, so every new row gets appended to the rightmost edge of the B-tree that backs the primary key index — cheap, sequential, cache-friendly. A random UUID v4 lands anywhere in that 128-bit space with equal probability, so every insert has to find a random leaf page somewhere in the middle of the tree, possibly one that got evicted from memory ages ago, and often has to split that page to make room. On an InnoDB table I've seen this play out directly: swapping a BIGINT primary key for a random UUID on a ~20 million row table roughly tripled the on-disk index size and measurably slowed bulk inserts, purely from page splits and fragmentation, with no change to the data itself. That's not a theoretical concern, it's a specific, measurable cost you're accepting the moment you pick a random primary key.
The practical answer isn't "always UUID" or "always auto-increment," it's matching the tool to the actual constraint. A single-database application with one writer and no need to hide record counts is well served by a plain auto-increment integer, and there's no reason to pay the UUID tax for nothing. A system with multiple writers, offline clients, or public-facing IDs benefits from a UUID's collision-free, information-free properties. And if you want both — merge-safety and decent index locality — UUID v7 or a similar time-ordered scheme splits the difference, because its leading timestamp bits mean new rows still land near the rightmost edge of the index instead of scattering randomly across it.
Try it
GlaeKit's UUID Generator generates cryptographically random UUID v4 identifiers in bulk, using your browser's crypto.randomUUID(). Nothing is sent to a server.
Frequently asked questions
Can two UUIDs ever actually collide?
Mathematically yes, practically no. A v4 UUID has 122 random bits, so the "birthday problem" math says you'd need to generate roughly 2.7 quintillion UUIDs before the odds of a single collision hit 50%. Generating a billion UUIDs a second, that still takes decades. No real application gets anywhere near that volume.
Is a UUID the same thing as a GUID?
Functionally, yes. GUID (Globally Unique Identifier) is Microsoft's name for the same 128-bit format, born out of the same underlying spec. In practice the two terms get used interchangeably, though Microsoft platforms tend to say GUID and everything else tends to say UUID.
Should I use a UUID as my database primary key?
It depends on whether you need merge-safety or ID unguessability more than you need index performance. If you have one writer and no reason to hide record counts, an auto-increment integer is simpler and faster. If you're merging data from multiple sources or exposing IDs publicly, a UUID (or a time-ordered variant like v7) is worth the storage and index cost.
What's the actual difference between UUID v4 and v7?
UUID v4 is fully random and gives no clue about when it was created. UUID v7 puts a millisecond-precision timestamp in its leading bits and fills the rest randomly, so v7 values sort roughly by creation time. That makes v7 better for database index locality while keeping most of v4's unguessability.
How do I generate a UUID v4?
In a browser or Node.js you can call crypto.randomUUID() directly, no library needed. Most languages have an equivalent built into their standard library or a near-universal package (Python's uuid.uuid4(), for example). GlaeKit's generator above does this in bulk if you just need a batch of them quickly.