Eternalsix

UUIDs in practice: v4, v7, and choosing an ID scheme that scales

Random IDs you can generate anywhere, with no coordination — but the version you pick has real consequences for your database. Here's how to choose.

At some point every system needs to name things: this user, that order, this uploaded file. The classic answer is an auto-incrementing integer from the database — 1, 2, 3. It's compact and fast, but it has a catch: only the database can hand out the next number, which means you can't know an entity's ID until you've written it, and you can't merge two datasets without collisions.

UUIDs — Universally Unique Identifiers — solve that by being large and random enough that any machine can generate one, any time, with a vanishing probability of ever colliding with another. That "generate anywhere, no coordination" property is why they're everywhere in distributed systems. But not all UUIDs are the same, and the version you choose has real performance consequences. Let's sort it out.

What a UUID looks like

A UUID is a 128-bit value, conventionally written as 32 hexadecimal digits in five hyphen-separated groups:

550e8400-e29b-41d4-a716-446655440000

That's 8-4-4-4-12 characters. Two positions carry meaning: one digit encodes the version (which generation algorithm produced it) and a couple of bits encode the variant (which layout standard it follows). Everything else is payload — random bits, timestamp bits, or both, depending on the version.

128 bits is an almost incomprehensibly large space: about 3.4 × 10³⁸ values. This is what makes "just generate a random one and assume it's unique" a safe bet in practice rather than a gamble.

The versions that matter

Several UUID versions exist, but in modern application code you'll almost always be choosing among three.

Version 4: pure random

UUIDv4 is 122 random bits (the rest is version/variant markers). It's the default almost everywhere, and for good reason: it needs no input, no state, and no coordination — just a good random number generator. If you call a uuid() function without thinking, you're probably getting a v4.

The collision math is reassuring. You'd need to generate on the order of a billion UUIDs per second for decades before a duplicate became likely. For practically every application, v4 uniqueness is a solved problem.

Its one weakness is not uniqueness — it's ordering, which we'll get to.

Version 7: time-ordered

UUIDv7 is the newer option (standardized in 2024's RFC 9562) and it's increasingly the recommended default for database primary keys. It puts a millisecond Unix timestamp in the high bits and fills the rest with randomness. The result is still globally unique and still generatable anywhere — but because the timestamp leads, v7 UUIDs sort in roughly the order they were created.

That single property fixes v4's big database problem (below) while keeping everything good about random UUIDs. If you're picking an ID scheme for a new table today, v7 is usually the right starting point.

Version 1: timestamp + MAC address

UUIDv1 also embeds a timestamp, but it traditionally mixes in the machine's MAC address, which can leak information about the host that generated it — a mild privacy and security concern. v7 is essentially the modern replacement: time-ordered without the hardware fingerprint. New systems should prefer v7 over v1.

The one that bites: random UUIDs and database indexes

Here's the practical reason the v4-vs-v7 choice matters, and it surprises people who assumed "an ID is an ID."

Most databases store their primary key in a B-tree index, and B-trees are happiest when new keys arrive in roughly increasing order — each insert lands near the "end," reusing recently-touched pages. Auto-increment integers do this naturally.

UUIDv4 is the opposite: every new key is random, so inserts scatter all over the index. On a large table this causes page splits and poor cache locality — the database constantly loads and rearranges index pages that were nowhere near each other. The symptom is insert performance that quietly degrades as the table grows, plus a larger, more fragmented index.

UUIDv7 sidesteps this entirely. Because the timestamp leads, new keys are monotonically increasing (approximately), so inserts append near the end just like an integer would. You get the "generate anywhere" freedom of a UUID with the insert locality of a sequential ID. This is the whole reason v7 exists, and why "use v7 for primary keys, v4 when you just need a random token" has become common advice.

When to use which

A practical decision guide:

A caveat on "unguessable"

A common temptation is to use a random UUID as a security token — a password reset link, an unsubscribe URL, an unauthenticated "anyone with the link" share. v4 is random enough that this is often fine, but with one caveat: a UUID is unique, not secret by design. It can end up in server logs, browser history, the Referer header, and analytics. For genuinely sensitive capabilities, treat a UUID like any other bearer secret — keep it out of logs, give it an expiry — or use a purpose-built cryptographic token. For "hard to guess so people can't enumerate each other's records," a v4 UUID does the job well.

Generating them

You rarely need to hand-write a UUID, but you often need one right now — a test fixture, a seed row, a config value, a placeholder ID while prototyping. A UUID Generator that runs in your browser gives you a fresh, correctly-formatted identifier on demand, which beats copying the same 00000000-... placeholder everywhere and later discovering two records share it.

The takeaway

UUIDs let any machine mint a globally-unique 128-bit ID with no coordination, which is why distributed systems love them. Reach for v7 as your default for database keys — it's unique and time-ordered, so it plays nicely with B-tree indexes instead of fragmenting them. Reach for v4 when you just need a random, order-independent token. And remember that "unique" isn't the same as "secret": a UUID is safe against guessing and enumeration, but for truly sensitive links, treat it with the same care as any other credential.