Comparison
UUID v4 vs v7: pick v7 for database keys
v4 for opaque tokens. v7 for everything else, especially database keys.
By Buğra SözeriPublished
TL;DR. UUID v4 is 122 bits of pure random and unsortable, which destroys B-tree index performance when used as a database key. UUID v7 (RFC 9562, 2024) prepends a 48-bit Unix-millisecond timestamp so the IDs sort by creation time. Use v7 for database keys and event IDs; stick with v4 for opaque session tokens.
UUIDs are 128-bit identifiers designed to be globally unique across systems without coordination. The original spec (RFC 4122, 2005) defined five versions; v4 (pure random) was the only one most developers used. RFC 9562 (2024) added v7, and v7 should be the new default for almost every use case where v4 was previously chosen.
The headline differences
| Property | v4 | v7 |
|---|---|---|
| Specified | RFC 4122 (2005) | RFC 9562 (2024) |
| Random bits | 122 | 74 |
| Timestamp embedded | No | Yes (48-bit ms since epoch) |
| Sortable by creation time | No (purely random) | Yes (lexicographic = chronological) |
| Collision probability | ~1 in 10³⁶ for any pair | Effectively zero (timestamp + random combined) |
| Database index friendliness | Poor (random insertion churns B-trees) | Excellent (sequential, append-friendly) |
| Browser support | crypto.randomUUID() since 2021 | Manual generation (no native API yet) |
Why v4 hurts databases
Database primary keys typically live in a B-tree index. B-trees perform best when inserts arrive in sorted order: new keys append to the rightmost leaf, no rebalancing needed, the index page stays warm in cache.
Random keys (v4) destroy all of that. Every insert lands in a random page, potentially causing a page split, certainly causing cache misses, and producing free space scattered across the index that never gets compacted. For write-heavy workloads this manifests as:
- 10-100× higher I/O than sequential keys at the same write rate
- Bloated index files (often 2-3× the size they’d be with sorted keys)
- Query slowdowns as cache hit rate degrades
The Postgres-vs-MySQL benchmarks comparing UUID v4 to bigint primary keys consistently show 2-5× write throughput difference on representative workloads. The fix isn’t to abandon UUIDs — it’s to use the kind of UUID that sorts.
What v7 looks like
A v7 UUID:
01950d29-4f6f-7234-bf01-a8b3c0d9e102 └─────timestamp_ms──────┘ └──random──┘
First 48 bits: Unix timestamp in milliseconds (so good until ~10889 AD). Next 4 bits: version (always 7). Next 12 bits: random. Then 2 bits of variant marker + 62 bits of random. Total random: 74 bits — comfortably collision- resistant.
Two v7 UUIDs created in the same millisecond compete on the 74 bits of random; collision probability inside that millisecond is roughly 10⁻²² for a billion-id system. Across milliseconds, the timestamp makes collisions structurally impossible.
When v4 is still the right choice
- Opaque session tokenswhere you don’t want creation order leaked. v7 leaks ~millisecond- resolution creation timestamps in the prefix, which is fine for database keys but bad for privacy-sensitive tokens.
- API keys, access tokens, password reset tokens: opacity matters more than sortability.
- Anonymisation identifiers: where observing the timestamp would help an attacker correlate.
When v7 is clearly better
- Database primary keys.
- Distributed-system IDs (event IDs, order IDs, trace IDs).
- Anywhere you’d benefit from being able to ORDER BY id and get chronological ordering for free.
- Anywhere range queries on creation time would otherwise need a separate index on created_at.
The migration path
New tables: use v7 from the start. Old tables: usually not worth migrating, but if you’re hitting write-throughput ceilings on a UUID-keyed table, switching to v7 is a worthwhile structural change.
Generate either via our UUID generator, which supports both versions.
Numeric facts
- String length: both are 36 characters when hyphenated (32 hex + 4 hyphens), 32 chars without hyphens, 22 chars in base64url, 16 raw bytes.
- Entropy: v4 has 122 random bits (6 bits consumed by version + variant markers); v7 has 74 random bits after the 48-bit timestamp.
- v4 collision math: 50% collision probability after generating ~2⁶¹ ≈ 2.3 × 10¹⁸ UUIDs — generate a billion per second and you’d wait ~85 years.
- v7 timestamp range: 48-bit Unix-millisecond field covers year 1970 to year 10889.
- Postgres B-tree benchmark (Percona, 2023): ~7,000 inserts/sec with v4 vs ~28,000 inserts/sec with v7/ULID on the same hardware and schema — a 4× gap that widens as the index grows past RAM.
- Index bloat: a 100 M-row v4-keyed table commonly ends up 2-3× larger on disk than the same data keyed with v7 or a sequential bigint, due to free space left after page splits.
- Generation speed:
crypto.randomUUID()(v4) runs at ~3 M ops/sec in Node 20; v7 implementations (uuid v9, ulid) clock ~2.5 M ops/sec.
Decision matrix
| Use case | Pick |
|---|---|
| Postgres / MySQL primary key | v7 (or ULID) |
| Distributed event ID, trace ID, order ID | v7 |
| Session token, CSRF token | v4 (opacity matters) |
| Password-reset / magic-link token | v4 + short TTL |
| API key prefix | v4 |
| S3 object key (sortable listing) | v7 |
| Idempotency key in webhook delivery | v7 (debug-friendly chronological order) |
| Public-facing slug | Neither — use NanoID or short hash |
Sources
- RFC 9562 — Universally Unique IDentifiers (UUIDs), May 2024 — rfc-editor.org/rfc/rfc9562 (defines v6, v7, v8; supersedes RFC 4122).
- Percona Blog — UUIDs in Databases: A Deep Dive, benchmarks v4 vs sequential keys — percona.com.
Frequently asked questions
- Is UUID v7 backwards compatible with v4?
- Yes — both are 128-bit identifiers in the same 8-4-4-4-12 hex format, both fit in any column or API that accepts a v4 UUID, and both encode their version in the same nibble. The migration is purely about how you generate new IDs; existing v4s in the database keep working unchanged.
- Does v7 leak the creation timestamp?
- Yes — the first 48 bits are the Unix millisecond timestamp, readable by anyone who sees the UUID. For most database keys this is fine or even useful, but for opaque session tokens or password reset codes where you don't want creation order exposed, stick with v4.
- Why does v4 hurt database performance?
- Because random keys land in random pages of a B-tree index on every insert, causing page splits and destroying cache locality. Sequential keys (like v7's timestamp prefix) append to the rightmost leaf and stay cache-friendly. Postgres and MySQL benchmarks typically show 2-5× write throughput difference.
- Can I generate v7 UUIDs in the browser?
- Not natively yet — crypto.randomUUID() returns v4. You need a small library (uuid v9+, ulid-based polyfills) or 10 lines of code combining Date.now() with crypto.getRandomValues(). Native v7 support is on the WHATWG roadmap.
Related
Published May 15, 2026