Part of: Browser-Based Developer Tools: Why Client-Side Beats Uploading

UUID v4 vs UUID v7: Which One Should Be Your Primary Key?

A scattered cloud of random IDs beside a neat ascending column of time-ordered IDs

For years the default answer to “which UUID?” was version 4: 122 random bits, no coordination, done. In May 2024, RFC 9562 replaced the original UUID standard and added version 7, which puts a timestamp at the front. That one change solves the biggest practical complaint about UUID primary keys. It also introduces a trade-off worth knowing before you switch.

You can generate both, and decode any UUID you have lying around, with the UUID Generator. It shows the version, the variant and, for time-based versions, the moment the ID was created.

What is inside each one

Every UUID is 128 bits, usually written as 32 hex digits in 8-4-4-4-12 groups. Six of those bits are fixed: four say which version it is (the first digit of the third group) and two mark the variant (the first digit of the fourth group is 8, 9, a or b).

Version 4 fills the other 122 bits with randomness:

919108f7-52d1-4320-9bac-f847db4148a8
              ^    ^
          version  variant

Version 7 spends the first 48 bits on a Unix timestamp in milliseconds, then fills the remaining 74 with randomness, or a counter plus randomness:

017f22e2-79b0-7cc3-98c4-dc0c0c07398f
└──────────┘  ^
 2022-02-22   version 7
 19:22:22 UTC

Because the timestamp comes first, and hex sorts the same way as the number it represents, v7 UUIDs sort in creation order as plain strings or bytes.

Why random keys hurt a database

Most databases store a primary key in a B-tree index. Sorted keys make inserts cheap: every new row lands on the rightmost page, which is already in memory, and pages fill up neatly one after another.

Random v4 keys land anywhere. Each insert may touch a different page, so the set of pages that has to stay in memory is the whole index rather than its tail. Pages split in the middle and end up half empty. Once the index is bigger than the cache, inserts start waiting on disk reads. Write amplification rises, and so does replication and WAL volume. None of this matters at ten thousand rows. It matters a great deal at a billion.

v7 keys behave almost like an auto-increment integer: new rows go at the end. You keep everything that made UUIDs attractive — IDs generated anywhere without a round trip, safe to merge across databases, not guessable in sequence — and lose most of the index penalty.

What v7 gives away

The timestamp is not hidden. Anyone who sees a v7 ID can read, to the millisecond, when it was created. Paste one into the generator’s decoder and it shows the date.

Often that is harmless, or even useful for debugging. Sometimes it is not:

  • A user ID in a public URL reveals when the account was made.
  • Order IDs reveal your order rate to anyone who places two orders.
  • Document or invite IDs leak timing that might correlate with other events.

For identifiers that face the outside world and where timing is sensitive, v4 is still the right choice. Some teams use both: v7 as the internal primary key, and a separate random v4 or opaque token in anything public.

Uniqueness: both are plenty

v4’s 122 random bits need about 2.7 × 10^18 IDs before there is even a 50% chance of one collision. v7 has fewer random bits (74), but they only have to be unique within the same millisecond. Generators also add a counter so that IDs made in the same millisecond still sort correctly. The generator on this site uses the 12-bit counter method from RFC 9562, which is why a bulk batch of v7 IDs comes out already in order.

Gotchas when you switch

  1. Store them as real UUIDs, not text. A native uuid column takes 16 bytes. CHAR(36) takes 36 or more and compares more slowly.
  2. SQL Server sorts uniqueidentifier strangely. It compares the last group of bytes first, so v7 values do not insert in order there without rearranging the bytes. PostgreSQL, MySQL BINARY(16) and most others compare byte by byte and are fine.
  3. Database support is recent. PostgreSQL 18 added a built-in uuidv7() function. On older versions, generate the ID in your application. Every major language has a well-maintained library.
  4. Clock trouble. A server whose clock jumps backwards can produce IDs that sort before earlier ones. Good generators, including this one, never let the timestamp go backwards.

The short version

  • Primary keys in your own database: v7.
  • Anything public where creation time should stay private: v4.
  • Existing v4 tables: switching new rows to v7 is safe. Both are valid UUIDs in the same column, and old rows simply keep their random order.

If the timestamp inside a v7 ID turns up in a log as a raw number, the Timestamp Converter turns milliseconds into a date. Unix timestamps explained covers the seconds-versus-milliseconds trap that catches most people. The generator runs entirely in your browser using the Web Crypto random number generator, for the reasons in browser-based developer tools.

← All articles