UUID v4 vs v7: when to use which

4 min read

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 caseRecommendationReason
Primary key, write-heavyv7Index efficiency
Primary key, low write volumev4 or v7Performance gap is small
Public URL IDv4Unguessability
External API request IDeitherDepends on context
Event log IDv7Time-sorted natively
Auth tokenneitherUse a dedicated CSPRNG, not UUIDs

Language and runtime support, as of 2026

Native v7 support varies:

  • Node.jscrypto.randomUUID() returns v4. v7 needs a package (uuid etc.) or your own implementation.
  • Python — the standard uuid module covers v1–v5 only. Use uuid7 or similar.
  • PostgreSQLuuid_generate_v7() from extensions, or community work toward making gen_random_uuid() produce v7.
  • MySQL — no native support; common practice is UUID_TO_BIN patterns.

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.