Version 4 is the safe default. Version 7 is the one your database wants.
A v4 UUID is 122 random bits with nothing else inside. No order, no structure, no meaning. Use one as a clustered primary key and every insert lands on a different page of the index. Pages split, the buffer pool fills with pages nobody is reading twice, and write throughput drops long before the table looks large. Anyone who has watched an InnoDB table slow down at ten million rows has met this problem.
Version 7 puts a 48 bit Unix millisecond timestamp in front of the random bits. Two v7 values made a second apart sort in the order they were created, so new rows append near the right edge of the index instead of scattering across it. You keep the part people want from UUIDs, which is minting a key without a round trip to the database, and you drop the part hurting the index. RFC 9562 standardised v7 in May 2024, and driver support has been landing steadily since.
| Version | What is inside | Random bits | Reach for it when |
|---|---|---|---|
| v4 | Random only | 122 | You want an opaque identifier and sort order does not matter |
| v7 | Millisecond timestamp, then random | 74 | The value becomes a primary key or a sort key |
| v1 | 100 nanosecond clock, clock sequence, node | 61 here | A legacy system or an existing schema expects v1 |
| v5 | SHA-1 of a namespace and a name | 0, fully deterministic | The same input has to map to the same ID on every machine |
| Nil | 128 zero bits | 0 | You need a sentinel meaning absent or unset |
| Max | 128 one bits | 0 | You need an upper bound in a range scan |
Where the random bits come from
Every random value on this page comes from crypto.getRandomValues(), which draws from the operating system cryptographic random source. That matters more than it sounds. Plenty of UUID pages still build v4 by mapping Math.random() across a template string. Math.random() is a fast pseudo random generator seeded once per browsing context, with no promise of unpredictability and a much smaller internal state than 122 bits. It is fine for shuffling a deck of cards on screen and wrong for anything you intend to treat as unique across machines.
If your browser exposes no secure random source, this page says so in a red bar at the top and refuses to generate rather than quietly falling back to something weaker.
Reading a UUID by eye
The 8-4-4-4-12 layout hides two fields worth knowing. The first character of the third group is the version digit. The first character of the fourth group is the variant.
018f3a2c-7b41-7c3e-9a10-5d2f6b8e4c11
| |
| variant nibble: 8, 9, a or b means the RFC layout
version nibble: 7 means a v7 time ordered UUIDA variant nibble outside 8 to b means the value follows the older NCS layout, a Microsoft GUID layout, or nothing standard at all. Old Windows registry GUIDs turn up with a variant of c or d often enough to matter. The inspector tab reports both fields, and for v1, v6 and v7 values it pulls the creation instant back out of the bits.
Collision odds, with the actual numbers
A v4 UUID has 122 random bits, so roughly 5.3 times 10 to the 36 possible values. The number worth quoting is not the space size though, it is the birthday bound. A 50 percent chance of a single duplicate arrives at about 2 to the 61 values drawn, near 2.3 quintillion. Generate a billion v4 UUIDs every second for a hundred years and you land in that neighbourhood.
Which is why duplicate UUIDs in production almost never come from the math. They come from a broken generator. Four causes account for most of them:
- A
Math.random()based generator seeded identically in every worker process at boot. - A container image cloned after its entropy pool was seeded, so every replica starts from the same state.
- A column too short, so
VARCHAR(32)silently truncates the hyphenated form and two rows collide on the first 32 characters. - Application code taking the first 8 characters as a short ID, which leaves 32 bits and a birthday bound around 77,000 values.
Check those four before you spend an afternoon on probability tables.
What a UUID is not
Five limits, each of which has bitten someone:
A UUID is not a secret. v1 and v7 carry a readable creation timestamp. Publishing v7 identifiers in URLs tells anyone reading them exactly when each record was made, which leaks signup order, order volume and growth rate. For a session token, a password reset link or an API key, use a random token from the password generator or the random byte generator instead.
The v1 values here use random node bits. A textbook v1 embeds the MAC address of the machine in the node field. A browser has no access to a MAC address and never will. This page sets the multicast bit and fills the remaining 47 node bits with random data, which RFC 9562 permits. The output is a valid v1 UUID, and it will not match what a server side library produces from real hardware. If you are trying to reproduce a specific v1 sequence, generate it server side.
The v1 clock is milliseconds wearing a costume. A real v1 counts 100 nanosecond intervals since 15 October 1582. JavaScript gives milliseconds. This page multiplies by 10,000 and adds a per millisecond counter so a burst of values stays ordered and unique. The last four digits of the timestamp field are bookkeeping, not measurement.
Version 3 is not offered. v3 hashes with MD5, v5 hashes with SHA-1, and RFC 9562 points new work at v5. Shipping both invites people to pick MD5 by accident. If a legacy system genuinely requires v3 for interop, a server side library is the right tool for the job.
Text storage costs more than you think. The canonical form is 36 characters. PostgreSQL uuid and MySQL BINARY(16) store the same value in 16 bytes. Keeping UUIDs in a VARCHAR(36) column more than doubles every index entry, and secondary indexes carry the primary key, so the cost multiplies across the table.
One habit worth keeping
Store lowercase, compare case insensitively. RFC 9562 says generators output lowercase hex and parsers accept either case. Half the duplicate key bugs involving GUIDs trace back to a Windows system emitting uppercase into a store treating the string as case sensitive.
Nearby tools
For raw entropy rather than a formatted identifier, the random byte generator and the random hex generator give you the bits without the version and variant framing. The random string generator covers short human readable codes where a 36 character value is too long, and the random number generator handles bounded integer ranges. Browse the rest under all generators.
