Skip to content
devtoolbox.cc

UUID v4 vs v7: which one should you use?

· 6 min read

Open the UUID tool

For years the default answer to “what id should I use” was UUID v4: generate 122 random bits, never coordinate with anyone, never collide. It still works. But on large tables, those random ids have a cost in index size and write speed, and in 2024 the new UUID spec made a time-ordered alternative official.

This article explains how UUIDs are structured, how to read the version from any UUID at a glance, and how to choose between v4 and v7. The UUID generator and inspector generates both versions and decodes any UUID you paste, including the timestamp inside a v7.

What a UUID is

A UUID is a 128-bit number, written as 32 hex digits in five groups:

017f22e2-79b0-7cc3-98c4-dc0c0c07398f

The groups are 8, 4, 4, 4 and 12 hex digits long.

The format and versions are defined by RFC 9562 (May 2024), which replaced the original RFC 4122. Two positions are fixed in every standard UUID:

  • The version is the first hex digit of the third group. In the example above that’s 7.
  • The variant is the first hex digit of the fourth group. For standard UUIDs it’s 8, 9, a or b. Here it’s 9.

So you can tell a UUID’s version without any tool: xxxxxxxx-xxxx-4xxx-... is v4, xxxxxxxx-xxxx-7xxx-... is v7.

The versions

Version How it’s built Use today?
v1 Timestamp (100 ns since 1582) + MAC address Legacy; leaks the machine’s MAC
v3 MD5 hash of a namespace and a name Prefer v5
v4 122 random bits Yes: the general-purpose default
v5 SHA-1 hash of a namespace and a name Yes, for deterministic ids from names
v6 v1 with the timestamp reordered for sorting Only for compatibility with v1 systems
v7 Unix timestamp in ms + 74 random bits Yes: the default for database keys
v8 Custom layout, vendor-defined Only for your own schemes

There are also two special values: the nil UUID 00000000-0000-0000-0000-000000000000 and, new in RFC 9562, the max UUID ffffffff-ffff-ffff-ffff-ffffffffffff.

v4: random

A v4 UUID is 122 random bits plus the 6 fixed version and variant bits. In JavaScript:

crypto.randomUUID(); // "620f0d6a-ede0-4e7b-acd2-c54147ac5291"

How likely is a collision? By the birthday bound, you’d need about 2.7 × 10^18 v4 UUIDs for a 50% chance of a single duplicate. At a billion ids per second, that’s around 86 years. In practice, collisions come from bad random number generators (a VM snapshot restored twice, a non-cryptographic Math.random() implementation), not from the math. Use the platform’s cryptographic generator and you can stop worrying.

The problem with random keys in databases

Most databases store primary keys in a B-tree index. B-trees are happiest when new keys arrive in order: each insert goes to the rightmost page, which stays in memory, and pages fill up completely.

Random v4 keys land at a random place in the index every time. On a table larger than memory, that means:

  • inserts touch pages all over the index, so fewer of them are cached and more reads hit disk
  • pages split in the middle, leaving them about half full, so the index grows larger than it needs to be
  • recently inserted rows sit far apart, so “latest rows” queries don’t benefit from locality

On small tables none of this is noticeable. On tables with hundreds of millions of rows, the difference in insert speed and index size can be large, so measure it on your own workload before and after a change.

v7: time-ordered

A v7 UUID starts with a 48-bit Unix timestamp in milliseconds, followed by the version, the variant and 74 random bits:

017f22e2-79b0-7cc3-98c4-dc0c0c07398f
^^^^^^^^ ^^^^                          48-bit timestamp (milliseconds)
              ^                        version: 7
                   ^                   variant: 8, 9, a or b

Everything else is random: 74 bits in total.

The first 12 hex digits are the time. For the example UUID above, 017f22e279b0 in hex is 1645557742000 milliseconds, which is 2022-02-22 19:22:22 UTC. The UUID inspector does that decoding for you, and the timestamp converter shows what the millisecond value means. (Reading hex digits as a number is exactly what the number base converter is for.)

Because the timestamp comes first, v7 UUIDs generated later sort after earlier ones, as strings and as bytes. For a database, that means new keys go to the end of the index, as with an auto-increment integer, while keeping every other UUID property: generated anywhere, no coordination, no collisions in practice.

Support

v7 is new enough that support varies:

  • PostgreSQL 18 added a built-in uuidv7() function. On older versions, use an extension or generate ids in the application.
  • Most languages have libraries (uuid on npm supports v7(), as do Python’s uuid6 package and Java’s uuid-creator). Python 3.14 adds uuid.uuid7() to the standard library.
  • crypto.randomUUID() in browsers and Node.js still only produces v4.

What v7 gives away

The timestamp is readable by anyone who sees the id. If your ids appear in URLs, a v7 reveals when each record was created, and roughly how many records per second you create if someone collects enough of them. For order numbers, user ids in public URLs or anything where creation time is sensitive, that may matter. v4 reveals nothing.

v7 is also less random: 74 bits instead of 122. That’s still far beyond what you need for uniqueness, but don’t use a UUID of any version as a secret token. Session ids, password reset links and API keys should come from a dedicated random generator with at least 128 bits of entropy.

Storage tips

  • Store UUIDs in a native type (uuid in PostgreSQL) or as 16 bytes (BINARY(16) in MySQL), not as 36-character text. Text takes more than twice the space and compares more slowly.
  • In MySQL, UUID_TO_BIN(uuid, 1) reorders v1 timestamps for better locality. With v7 you don’t need the swap; the order is already right.
  • Case doesn’t matter. 017F22E2-... and 017f22e2-... are the same UUID, but compare them after normalising, or in a native type.

Which one to use

Situation Choice
Primary keys in a large, write-heavy table v7
Ids exposed publicly where creation time is sensitive v4
The same input must always produce the same id v5 (name-based)
Nothing special, small tables, existing v4 everywhere v4 is fine, no need to migrate

Need a batch of ids for a test fixture, or want to check what’s inside an id from a log? The UUID generator creates up to 1,000 v4 or v7 UUIDs at once and decodes the version, variant and timestamp of any UUID you paste.

← All posts