Xerobit
Encoding & Crypto

UUID v4 vs v7 for Databases: The Benchmark You Need

UUID v4 fragments your primary key index. UUID v7 fixes it with millisecond-ordered timestamps. Here's the benchmark data, why it matters, and when to switch.

MA Mian Ali Khalid 8 min read
Free tool
UUID Generator
Generate UUID v4 and v7 identifiers in bulk.
Open tool →
On this page ⌄
  1. The B-tree problem with random UUIDs
  2. Benchmark numbers
  3. What UUID v7 looks like
  4. Generating UUID v7
  5. When to use v4 vs v7
  6. UUID v7 vs ULID vs NanoID
  7. Migrating from v4 to v7
  8. Further reading

UUID v4 generates 122 bits of randomness. That's its entire feature set. It's collision-resistant, globally unique, and completely unordered. For decades "use UUID v4 as your primary key" was treated as obvious good practice.

It causes a measurable, documented performance problem in databases at scale, and UUID v7, finalized in RFC 9562 (May 2024), fixes it without giving up uniqueness or collision resistance.

The UUID Generator on this site supports both v4 and v7. Here's which one to reach for and why.

The B-tree problem with random UUIDs

Almost every production database uses B-tree indexes for primary keys: PostgreSQL, MySQL, SQLite, SQL Server. B-trees maintain keys in sorted order in fixed-size pages (typically 8-16 KB).

When you insert a row with a sequential or monotonically increasing key, the new key goes to the rightmost page of the index, the "hot page." Writes cluster at the end. This is cache-friendly: the hot page stays in memory, and the rest of the tree doesn't need to be touched.

When you insert a row with a random UUID v4 key, the new key can land anywhere in the index. With 10 million rows and 8 KB pages, there are roughly 50,000+ leaf pages. A new UUID v4 insertion hits a random one. If that page has been evicted from the buffer pool (which it has, you haven't touched it recently), the database must read it from disk, insert the row, mark the page dirty, and potentially split it if it's full.

This causes three cascading problems:

  1. Page splits: random inserts fill pages out-of-order, causing frequent splits that fragment the tree
  2. Buffer pool thrash: writes scatter across thousands of pages, none of which stay hot
  3. Write amplification: each insert triggers random reads before the write can happen

At small scale (under ~1 million rows), none of this is noticeable. At 10-100 million rows with moderate write throughput, the effect is real.

Benchmark numbers

Markus Winand (Use The Index, Luke) and several PostgreSQL / MySQL community benchmarks document roughly a 2-3x throughput penalty for random UUID v4 vs sequential integer primary keys on INSERT-heavy workloads. The exact penalty depends on:

  • Table size vs buffer pool size (once the index fits in memory, penalty shrinks)
  • Write/read ratio
  • Storage type (SSDs reduce the random-read penalty)

UUID v7, because it embeds a millisecond timestamp in the high bits, produces monotonically increasing values within a millisecond window. New rows cluster at the end of the index. The fragmentation problem disappears.

What UUID v7 looks like

UUID v7 (RFC 9562) format:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
├─────────────────────────────────────────────────────────────────┤
│                    unix_ts_ms (48 bits)                        │
├─────────────────────────────────────────────────────────────────┤
│  ver(4) │      rand_a (12 bits)                                │
├─────────────────────────────────────────────────────────────────┤
│  var(2) │      rand_b (62 bits)                                │
└─────────────────────────────────────────────────────────────────┘

The first 48 bits are the Unix timestamp in milliseconds. The remaining 74 bits are random. This means:

  • Values generated within the same millisecond differ only in the random portion: still globally unique
  • Values generated across milliseconds are strictly monotonically ordered
  • The UUID is still globally unique: the 74-bit random portion provides more than enough collision resistance

A v7 UUID looks like 019602e4-8d32-7f4b-8c0a-3b4e9a1f2d56. The timestamp prefix means it sorts in creation order in any lexicographic comparison, including string-typed columns.

Generating UUID v7

UUID v7 is new enough that not all libraries support it natively yet.

// Node, uuid package (v10+)
import { v7 as uuidv7 } from 'uuid';
const id = uuidv7();

// Or use the @uuid-rock/uuid7 package
import { uuidv7 } from '@uuid-rock/uuid7';
# Python 3.12+
import uuid
id = uuid.uuid7()

# Python < 3.12: uuid7 package
from uuid7 import uuid7
id = uuid7()
// Go, google/uuid
import "github.com/google/uuid"
id := uuid.Must(uuid.NewV7())

PostgreSQL doesn't have a native gen_uuid_v7() function yet, but you can use an extension or generate them in application code.

When to use v4 vs v7

Use v7 when:

  • The UUID is a primary key on a table that gets significant write volume
  • You want lexicographic ordering to reflect creation order (useful for pagination, debugging)
  • You're on a new project with no legacy constraints

Use v4 when:

  • The UUID is not a primary key (foreign keys, session tokens, correlation IDs)
  • You need opaque, unguessable identifiers where embedding a timestamp is a privacy concern (the timestamp in v7 reveals approximate creation time)
  • You're maintaining an existing v4 system and the migration cost isn't worth it

The privacy consideration: UUID v7 embeds the millisecond creation timestamp in the first 48 bits. If you're using UUIDs as public-facing identifiers (e.g., in URLs), this leaks when the record was created. For user-facing IDs where you want to obscure creation time, v4 is the right choice.

UUID v7 vs ULID vs NanoID

PropertyUUID v7ULIDNanoID
StandardRFC 9562Informal specNo RFC
OrderedYes (ms)Yes (ms)No
Collision-resistant74-bit random80-bit randomConfigurable
String form36 chars (hyphenated)26 chars (Crockford base32)21 chars (default)
DB type supportNative UUID type in PG/MySQLStore as varcharStore as varchar
Sortable in UUID type columnYesNo (needs varchar)No

For most database use cases, UUID v7 is the best choice: it has a formal standard, native UUID column type support, and the same ordered-insertion property as ULID without requiring a custom string format.

Migrating from v4 to v7

You cannot change existing primary keys without a full-table migration. The practical approach:

  1. New tables: use v7 from the start
  2. Existing high-write tables: generate a migration plan: add a new v7 column, backfill, swap references, drop old column. Painful but worth it for tables > 10M rows with write-heavy workloads
  3. Low-write tables: v4 is fine to leave; the performance difference is negligible

The UUID Generator generates both v4 and v7, use it to inspect the format and understand the timestamp prefix.

Further reading

Keep reading

CUID2: Collision-Resistant IDs Better Than UUID v4

CUID2 generates secure, URL-safe, database-friendly IDs with better collision resistance than UUID v4. Learn how CUID2 differs from UUID, nanoid, and ULID, with JavaScript and...

NanoID vs UUID: Which Unique ID Generator Should You Use?

NanoID generates shorter, URL-safe unique IDs using a custom alphabet. UUID v4 is the standard 128-bit identifier. Here's when to use each and how they compare on size, speed,...

ULID Generator: Universally Unique Lexicographically Sortable Identifiers

ULID is a 26-character sortable unique identifier that embeds a millisecond timestamp prefix. Unlike UUID v4, ULIDs sort chronologically. Here's how ULIDs work and when to use...

UUID Collision Probability: How Likely Are Duplicate UUIDs?

UUID v4 has 122 bits of randomness. Collisions are theoretically possible but astronomically unlikely. Here's the math behind UUID uniqueness and when collision risk actually...

UUID Format: Understanding the 128-Bit Unique Identifier Structure

UUIDs follow a specific 8-4-4-4-12 hexadecimal format defined by RFC 4122. Here's what each section of a UUID means, the version and variant bits, and how to parse UUID fields.

UUIDs in Databases: Performance, Storage, and Best Practices

UUIDs as primary keys have real performance implications in PostgreSQL and MySQL. Learn UUID storage size, index fragmentation with random UUIDs, why UUID v7 is better than v4...

UUID Versions Explained: v1, v3, v4, v5, and v7

UUID has multiple versions with different generation strategies. Learn when to use UUID v4 (random), v5 (name-based SHA-1), v7 (time-ordered), and why v1 and v3 are largely...

UUID v4 Generator: Random UUIDs Explained

UUID v4 uses random bits to generate universally unique identifiers. Here's how v4 differs from other UUID versions, what makes them unique, and when to use UUID v4 vs...

Related tools

Written by Mian Ali Khalid. More guides in Encoding & Crypto.