Uttir
By Uttir 6 min read

UUID v4 vs v7: When to Use Each (and Why It Matters in 2026)

UUID v7 is the 2024 successor to v4 for database primary keys. Learn what changed, why it exists, when to keep v4, and how to migrate without breaking anything.

UUID v4 is 122 random bits — fine for tokens, opaque IDs, and hash-partitioned stores, but terrible as a database primary key on a B-tree index because every insert lands at a random position and fragments the index. UUID v7 (RFC 9562, 2024) puts a 48-bit millisecond timestamp in the high bits, so new rows always go to the right edge of the index and inserts stay fast as the table grows. Default to v7 for database primary keys; keep v4 for tokens, public IDs, and anywhere the timestamp would be a privacy leak.

For most of the 2010s, the default answer to "how should I generate a unique ID" was UUID v4 — random, opaque, works everywhere. Then people started putting billions of v4 UUIDs into relational databases and noticed something interesting: the inserts were getting slow in a way that did not happen with auto-incrementing integers. UUID v7, standardized in RFC 9562 in 2024, is the answer. This guide explains what changed, when to use which, and the practical migration question for any system that already has v4 primary keys.

The problem v4 leaves behind

UUID v4 is 122 bits of randomness, with 6 bits fixed for the version (4) and variant markers. Two v4 UUIDs generated independently are unique for any realistic deployment — the collision probability is so low that you can treat it as impossible. They are also the easiest ID to generate: most languages have a one-liner for it, and the result is opaque enough that no information about the creator leaks through.

The problem shows up when v4 UUIDs are used as the primary key of a B-tree-indexed table — which is the default for every relational database. A B-tree stores rows in sorted order by the primary key. When new rows are inserted with v4 keys, each one lands at a random position in the sorted sequence. The database has to split index pages, shuffle data around, and keep the working set of the index large enough to cover the random access pattern. As the table grows past a few million rows, the cost adds up: insert throughput drops, the index stops fitting in cache, and backups and restores slow down because the data is no longer clustered on disk.

This is not a theoretical problem. The PostgreSQL and MySQL teams have both published benchmarks showing 2–10× insert throughput differences between v4 and a time-ordered alternative. The difference is invisible on a laptop with a few thousand rows; it shows up in production under write pressure with an index that no longer fits in RAM.

What v7 changes

UUID v7 keeps the 128-bit size and the familiar xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format, but partitions the bits differently:

  • 48 bits of Unix-epoch milliseconds in the most significant position.
  • 4 bits for the version (the number 7).
  • 12 bits of sub-millisecond randomness, for ordering of IDs created in the same millisecond.
  • 2 bits for the variant (RFC 4122, same as v4).
  • 62 bits of additional randomness.

The high bits are time, so UUIDs generated close together are close together in sort order. The trailing 74 bits of randomness see to uniqueness. The result looks like a UUID, parses as a UUID, and stores in the same 16-byte column — but the sort order is now meaningful.

For a database primary key, the practical effect is:

  • New rows go to the right edge of the B-tree, where there is free space. No random page splits.
  • The index stays small and sequential on disk. Backups and restores are faster.
  • Range queries by creation time work directly on the primary key, without a separate created_at index.

The trade-off: time leakage

The cost of v7 is information leakage: anyone with two v7 IDs can estimate how far apart in time they were created, to millisecond precision. For an internal row ID, that is a feature (you can extract the timestamp from the ID itself). For a public identifier — a user-visible invite code, a password-reset token, a public object handle — that is a privacy problem.

The right rule:

  • Use v7 for internal database primary keys, event IDs, log correlation IDs, anything that lives behind your auth boundary and benefits from time ordering.
  • Use v4 for tokens, API keys, password-reset URLs, public object handles, anything the user can see or guess.

If you need both, generate the v7 ID for the database and a separate v4 ID for the public-facing API. The v4 ID is what the URL contains; the v7 ID is what the database index is keyed on. A simple join on a single column keeps them in sync.

What RFC 9562 gives you beyond v7

RFC 9562 (the 2024 standard that replaced RFC 4122) is not just v7. It also defines v6, v8, and a few others. The two worth knowing:

  • UUID v6 — a reworked version of v1 with a different timestamp encoding, kept for compatibility with v1 consumers. If you have v1 UUIDs in production and need to upgrade, v6 is the migration target. If you are starting from scratch, skip to v7.
  • UUID v8 — a "custom" format where the version field is set to 8 and the rest of the bits are yours. Useful for embedding application-specific data (a region code, a tenant ID, a custom timestamp format) without breaking UUID compatibility.

The vast majority of new systems should use v7. The decision points for the other versions are: "do I have existing v1 UUIDs to migrate?" → v6; "do I have a custom format my database needs to support?" → v8; "neither" → v7.

Library support in 2026

Most languages now ship v7 in their standard library or in their most common UUID package:

  • JavaScriptcrypto.randomUUID() in Node 19+ and modern browsers produces v4. For v7, the uuid package (v9+) and uuidv7 both generate v7 IDs.
  • Pythonuuid.uuid7() is in the standard library from Python 3.14 (released October 2024). For earlier versions, the uuid7 backport package works on 3.8+.
  • Gogoogle/uuid v1.6+ has a NewV7() function.
  • PostgreSQL — the pg_uuidv7 extension or built-in uuidv7() in PostgreSQL 18+.
  • MySQL — no native support yet; use the application or a custom function.

If you are building a new service in 2026, the library question is mostly settled: v7 is available everywhere you would realistically use it.

What to do about existing v4 primary keys

The honest answer for most teams: nothing, immediately. v4 UUIDs work fine. The performance difference only shows up at scale, and only if v4 is the bottleneck — not "the database is slow", but "I have profiled the slow queries and the primary-key index is the hot path".

If you have confirmed v4 is the bottleneck, the migration is straightforward but it takes planning:

  1. Generate v7 IDs at the application layer going forward, but keep the v4 column as the primary key. Index a v7 column alongside it.
  2. Backfill the v7 column for existing rows by reading the v4 ID (or a created_at if you have one) and generating a v7 from the timestamp. The new v7 values are deterministic per row, so the backfill is idempotent.
  3. Switch read paths to use the v7 column for range queries and time-ordering.
  4. Switch insert paths to generate v7 for the v7 column and v4 for the v4 column (or migrate the primary key itself, which is a bigger change).

This is a "plan it for a quarter, not a sprint" migration. Most teams will not need it.

The decision matrix

For a new system, the choice is simpler than it sounds:

ScenarioUse
Database primary key (B-tree indexed)v7
API request ID / correlation IDv7
Event sourcing / append-only logv7
Distributed log / event IDv7
Session token / API keyv4
Password reset tokenv4
Hash-partitioned store (MongoDB, DynamoDB)v4
Public URL handlev4 or purpose-built random token
Existing v1 UUIDs you need to upgradev6

Try them in the browser

Both formats are now first-class options in most languages, but for a quick sanity check, a v4 UUID generator and a v7 UUID generator in the browser will let you see the difference in the output. The v4 IDs are all-random; the v7 IDs start with the same first segment for IDs generated in the same millisecond. That visual difference is exactly the property that makes v7 a better database key.

UUID v7 is the biggest change to UUIDs in 20 years, but the decision is not complicated: use v7 for any internal primary key that will scale past a few million rows, use v4 for anything that needs to stay opaque, and pick the right tool for the job. The library support is here, the standard is here, and the performance reason is clear.

#uuid#database#developer-tools#primary-key#rfc-9562

New tools and guides, once a week

One short email when something new ships. No tracking, no images, unsubscribe with one click.