UUID v4 vs UUID v7: Which Should You Use?
UUID v4 is fully random; v7 is time-ordered. Here's the bit-layout difference, why it matters for database index locality, and when to pick each.
The short answer
UUID v4 is 122 bits of randomness with a version tag stapled on. UUID v7 spends the first 48 of those bits on a millisecond Unix timestamp, so the values it produces sort roughly in the order they were created. If you’re generating primary keys for a database table that gets a lot of inserts, v7 will usually serve you better. If you need an identifier that reveals nothing about when it was created — or you’re stuck on infrastructure that only knows v4 — stick with v4. Both are formally defined in RFC 9562, which superseded the original UUID spec in 2024.
You can generate either with the UUID Generator.
What each one actually is
A UUID is a 128-bit value. Four of those bits are a version marker and two more are a variant marker, leaving 122 bits for everything else — what fills those 122 bits is the whole story.
UUID v4 fills essentially all of them with random bits. There’s no structure to exploit and no information encoded — a v4 UUID tells you nothing except that it’s a v4 UUID. It’s an opaque, unguessable label.
UUID v7 carves out the first 48 bits for a Unix timestamp in milliseconds. After the version and variant bits, the remaining ~74 bits are random (the spec allows implementations to use some of that space as a sub-millisecond counter for strict ordering within the same millisecond). The result looks structurally similar to v4 — same 128-bit size, same textual format — but sorts approximately in creation order, because the high-order bits dominate lexical/numeric sort order.
Why time-ordering matters for databases
This is the actual reason v7 exists. Most databases store rows in roughly the physical order of their primary key, using a B-tree or similar structure. If your primary key is a v4 UUID, every insert lands at a random point in that structure — which forces more page splits (costing write throughput and bloating the index), scatters recently inserted rows across disk instead of clustering them (slowing range scans over “recent rows”), and touches more distinct pages per insert than a sequential key would.
A v7 UUID, because its timestamp bits dominate, behaves close to an auto-incrementing integer for insert order while still being generateable client-side without coordinating with a database. You get the locality benefits of sequential keys without giving up the benefits of UUIDs — no central counter, safe to generate offline or across distributed nodes.
When v4 is still the right call
Time-ordering is a feature until it’s a liability. Don’t switch to v7 if:
- The ID needs to be unguessable, not just unique. A v4 UUID leaks nothing. A v7 UUID leaks the millisecond it was created — that can matter for invite codes or anything where knowing “this ID was minted at 14:03:07 on a Tuesday” is useful to an attacker. Never use a v7 UUID (or any UUID) as a bearer secret; that’s what a Secure Token Generator is for.
- You shard by hashing the raw UUID’s leading bytes. For v4 that’s fine because those bytes are random. For v7 those bytes are timestamp bits, so all inserts in a given time window pile onto the same shard. Hash the whole value first, or shard on something else.
- Your stack doesn’t support it yet. v7 is new enough (finalized 2024) that older ORMs or
uuidlibraries may not generate it yet. The column type doesn’t need to change — it’s still 128 bits — only the generator does.
Migration notes
Because v4 and v7 are both plain 128-bit values stored the same way, migrating is a generation-side change, not a schema change. You can dual-write — keep issuing v4 on existing flows while switching new code paths to v7 — and existing rows never need rewriting; mixed-version columns are legal, you just lose the ordering benefit on the old rows. Audit anywhere that treats a UUID as “definitely high-entropy and uninformative,” such as rate limiters or cache keys derived from UUID prefixes, to confirm nothing implicitly depended on v4’s total randomness.
Collision fear-mongering, addressed with actual math
People sometimes worry that time-based UUIDs “collide more” than fully random ones. Let’s do the arithmetic instead of gesturing at it.
A v4 UUID has 122 random bits. Using the birthday-bound approximation, you’d need to generate roughly 2.71 × 10¹⁸ UUIDs (2.71 quintillion) before the probability of any collision hits 50%. Generating a billion v4 UUIDs per second, that’s about 85 years to reach that count — for even odds of one collision across the entire set.
A v7 UUID has fewer purely random bits (around 74, versus v4’s 122), but each millisecond effectively gets its own random-collision pool, since two UUIDs generated in different milliseconds already differ in the timestamp bits. Within a single millisecond, 74 bits of randomness across the handful of UUIDs any real system generates is still astronomically safe. The “v7 is less safe” claim usually confuses “fewer random bits” with “meaningfully higher collision risk,” which doesn’t hold up once you run the numbers.
Nil, Max, and name-based UUIDs, briefly
A few other UUID variants are worth knowing about, even outside the v4-vs-v7 decision:
- Nil UUID:
00000000-0000-0000-0000-000000000000— all zero bits, used as an explicit “no value” sentinel. - Max UUID: all-
fbits, formalized in RFC 9562 as the practical opposite of nil, occasionally used as an upper-bound sentinel in range queries. - UUID v3 and v5: name-based, not random at all. Feed in a namespace UUID plus a name string and get a deterministic UUID out (v3 uses MD5, v5 uses SHA-1) — same inputs always produce the same UUID, useful for deriving a stable ID from other data without a lookup table.
FAQ
Is UUID v7 a replacement for auto-increment integers? For many use cases, yes — most of the locality benefit, without a central counter. It’s still 128 bits on disk versus 4 or 8 for an integer, so a huge, join-heavy table may still favor a plain integer key.
Can I tell someone’s exact request time from a v7 UUID? Yes, the millisecond it was generated. Treat that as public information, not a secret — use v4 or an opaque token if timing needs to stay hidden.
Do I need to migrate existing v4 primary keys to v7? Only if insert performance or index bloat is an actual problem on that table. Small or write-light tables aren’t worth migrating.
Are v4 and v7 UUIDs interchangeable in code? Yes — both are 128-bit values in the standard 8-4-4-4-12 hex format. Application code doesn’t need to know which version it’s holding unless it’s specifically parsing out the timestamp.