UUID v4 vs v7: when to use which
UUIDs (Universally Unique Identifiers) are the standard way to generate identifiers that won’t collide across systems. v4 (random) has long been the de-facto default, and in 2024 RFC 9562 was published, formalizing v7 and giving us a stronger option. This article compares the structural differences and the cases where one beats the other.
The 128-bit shape every UUID shares
Every UUID is 128 bits (16 bytes), serialized as 32 hexadecimal characters split into 8-4-4-4-12 groups by hyphens.
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
↑ ↑ ↑
| | └─ variant (top bits identify the format family)
| └────── version (4, 7, …)
└─────────── version-dependent payload The first nibble at position M is the version, and the top bits at position N are the variant. The same 128 bits mean different things depending on the version.
v4: pure randomness
v4 fills 122 random bits (the rest is fixed by version + variant) with cryptographically strong randomness:
6e8b3c1d-4f2a-4b9d-8c1e-5a7f9d2b4c6e
↑ ↑
version=4
variant Properties:
- Trivial to generate — just call your CSPRNG.
- Unguessable — embedding one in a URL like
/api/orders/<uuid>does not leak the existence of nearby IDs. - No temporal information — the UUID itself does not reveal when it was created.
- Practically zero collision risk — 122 bits of randomness is well past the birthday-paradox threshold for any realistic workload.
This combination of simplicity and safety is why v4 became the workhorse.
v7: timestamp + randomness
v7 places a 48-bit Unix millisecond timestamp at the front and fills the rest with random bits:
01970000-0000-7xxx-Nxxx-xxxxxxxxxxxx
└──────┬──────┘ ↑ ↑
48 ms ts version=7
variant + 74 random bits Properties:
- Sortable in time order — lexicographic sorting of the string mirrors creation order.
- Friendly to database indexes — primary keys land at the tail of the B-tree, avoiding scattering.
- Carries time information — the creation time can be derived from the ID.
- Monotonicity within a millisecond — some implementations encode a counter in the random suffix to enforce strict ordering for IDs minted in the same millisecond.
The big difference: B-tree index behavior as a primary key
The most concrete operational difference between v4 and v7 is how they behave inside a B-tree index. Most relational databases (PostgreSQL, MySQL/InnoDB, …) store primary keys in a B-tree, with each insert landing somewhere in the structure.
v4 as primary key
Random keys land at random positions in the B-tree:
- Page splits become frequent when full pages get new neighbors.
- Cache hit rates suffer because hot pages are scattered.
- High-volume insert workloads slow down noticeably.
v7 as primary key
Time-ordered keys always append at the tail:
- Page splits are essentially absent.
- Recent rows cluster together, friendly to the buffer pool.
- Index efficiency approaches that of an auto-increment integer key.
For write-heavy workloads, v7 can be several times faster than v4 in practice.
The unguessability tradeoff
Because v7 contains the creation timestamp, it leaks more information than v4.
If you expose a v7 UUID like /orders/<uuid-v7>/, an attacker can read off “when was this order placed”. Mitigations if that matters:
- Truncate the v7 timestamp precision (e.g. round to the second).
- Use v4 (or short opaque tokens) for externally facing IDs while keeping v7 internally.
- If ordering doesn’t matter, v4 is the simpler choice.
For tokens, API keys, and other unordered cases, v4 remains the safer pick.
Quick decision table
| Use case | Recommendation | Reason |
|---|---|---|
| Primary key, write-heavy | v7 | Index efficiency |
| Primary key, low write volume | v4 or v7 | Performance gap is small |
| Public URL ID | v4 | Unguessability |
| External API request ID | either | Depends on context |
| Event log ID | v7 | Time-sorted natively |
| Auth token | neither | Use a dedicated CSPRNG, not UUIDs |
Language and runtime support, as of 2026
Native v7 support varies:
- Node.js —
crypto.randomUUID()returns v4. v7 needs a package (uuidetc.) or your own implementation. - Python — the standard
uuidmodule covers v1–v5 only. Useuuid7or similar. - PostgreSQL —
uuid_generate_v7()from extensions, or community work toward makinggen_random_uuid()produce v7. - MySQL — no native support; common practice is
UUID_TO_BINpatterns.
For new projects choosing a primary key strategy, v7 is worth a serious look. The UUID generator on this site can produce both v4 and v7 if you want to compare the formats side by side.