UUID v4 vs v7: Choosing a Primary Key

Published 2026-10-31

Both are 128-bit identifiers with negligible collision risk. Both are copy-paste safe. The choice comes down to one property: is the value sortable by creation time?

That property decides how your database indexes behave at scale.

What is actually inside

v4 v7
Layout 122 random bits 48-bit Unix ms timestamp + 74 random bits
Sortable by time No Yes
Guessable No Partially — leaks creation time to the millisecond
Standardised RFC 4122 RFC 9562 (2024)
Index behaviour Scattered inserts Appends

v4 is pure randomness. v7 puts the timestamp first, then randomness, which means lexicographic order equals creation order.

Why the layout matters: index locality

A B-tree index keeps keys in sorted order. When you insert a new row:

  • Sequential key (auto-increment, v7) → the new key goes at the end, which is almost always in the cache. One page touched.
  • Random key (v4) → the new key lands in a random page, which is likely not in memory, so the engine must read it from disk, modify it, and write it back.

That random write is not a theoretical concern. On a table with a few hundred million rows and a working set larger than RAM, v4 primary keys reduce insert throughput markedly and inflate the index (pages split more often, fill factor drops).

This is the same argument that makes UUIDv7 and ULID attractive: they are "UUID-shaped" but behave like an auto-increment for inserts.

When v4 is the right answer

Randomness is a feature when the identifier is exposed to users:

  • Public URLs and share links. A v7 reveals when the resource was created, to the millisecond. For a support ticket, that is a minor leak; for a "shared document" link, it is metadata you would rather not publish.
  • Guessing resistance. v7 is not guessable in practice (74 random bits follow the timestamp), but v4 is trivially unguessable.
  • Anything that must not correlate with creation time — for example, an ID used to bucket users, where a time-derived prefix leaks the growth curve.

A common compromise: v7 as the internal primary key (good index behaviour), plus a v4 public token in a separate column for external references.

What v7 leaks

The first 48 bits are a millisecond timestamp. Given one ID you can recover the creation time exactly. That is usually acceptable, and sometimes useful — but it must be a decision, not an accident.

If the leak matters and you still want sortable keys, ULID (also time-prefixed, Crockford base32) has the same property, so switching does not help. The alternative is a random key plus a separate created_at column and a covering index — which reintroduces the scattered insert.

Generating them

crypto.randomUUID()        // v4, available in Node 19+ and all modern browsers

For v7, most runtimes do not ship it yet; use a small library or construct it: 48-bit timestamp, version nibble, 12 random bits, variant, 62 random bits.

Two rules regardless of version:

  1. Use a cryptographic source. Math.random() is not one — collisions and predictability both follow from that.
  2. Never derive a UUID from user data (email, name) with a hash. That identifies users across systems and cannot be rotated.

UUID Generator produces v4 and v7 in bulk, with options for uppercase, no hyphens, and random tokens — handy when you need a column of test IDs or a batch of API keys.

The decision table

Situation Use
High-volume table, key never exposed v7 (or auto-increment)
Key appears in user-facing URLs v4
Existing schema with v4 Keep it; do not migrate for this alone
Need to sort by creation without a timestamp column v7
Distributed writers, no central sequence v7

The migration advice matters: changing a primary key type on a large table is expensive and risky. If v4 is already working, the insert-locality argument is usually not worth a rewrite — apply it to new tables instead.

Summary

v7 buys insert locality at the cost of leaking creation time. v4 buys unpredictability at the cost of random writes. Neither is "better"; they encode different priorities. Pick based on whether the key is public and whether the table will be large.