UUIDs: Which Version to Use, and Why v4 Is Usually Wrong for a Primary Key

The versions that matter, the collision maths, and the index fragmentation nobody warns you about.

A UUID is 128 bits, written as 32 hex digits in five groups: 550e8400-e29b-41d4-a716-446655440000. The point is generating an identifier without asking anyone's permission — no central sequence, no coordination — and still not colliding with identifiers generated elsewhere.

Reading a UUID

Two positions carry meaning. The first digit of the third group is the version; the first digit of the fourth group encodes the variant and is almost always 8, 9, a or b.

550e8400-e29b-41d4-a716-446655440000
                              version  variant

So that one is a version 4. Of the 128 bits, 6 are spent on version and variant, leaving 122 that actually vary.

The versions worth knowing

The collision maths

122 random bits is about 5.3×1036 values. By the birthday bound, a 50% chance of one collision needs roughly 2.7×1018 UUIDs — and generating a billion a second, that is around 85 years. For a 1-in-a-billion chance of any collision you can generate about 100 trillion of them.

The practical conclusion is that v4 collisions are not a real risk provided the randomness is real. That proviso is where actual failures come from: a poorly seeded PRNG, a language's non-cryptographic random, or containers starting from identical state. Use the platform's crypto-random source, not its general-purpose one.

The problem v4 causes in a database

This is the part that bites in production. Most databases store rows in primary-key order in a B-tree. A sequential key appends to the end — one hot page, tight packing. A random key inserts at a uniformly random position, which means:

On a large table the difference in insert throughput is not subtle. This is precisely what v7 fixes: the leading timestamp restores ordering, so inserts land at the end again while the trailing random bits keep them unguessable and uncoordinated.

If you are choosing today for a table that will grow, choose v7. If you are on v4 and the table is small, it does not matter.

Storage

The canonical text form is 36 characters. Stored as raw bytes it is 16 — less than half, and worth doing on a large table (BINARY(16) in MySQL, the native uuid type in PostgreSQL). Storing a UUID as VARCHAR(36) with a default collation is the common and costly default.

What a UUID is not

It is not a secret and it is not an authorisation. A v4 UUID is unguessable, which tempts people into using one as a capability token; the difference is that UUIDs get logged, appear in referrer headers and end up in analytics. It is also not a hash — a v4 carries no information about what it identifies, which is the point, but means it cannot be recomputed if lost.

Generate UUIDs → · Related: hashing · Back to all articles