When you need a unique identifier, UUIDs are the default. But v4 or v7? That question got a lot more interesting after RFC 9562 landed in 2024. v7 isn't just "a newer version." It addresses the core pain point v4 creates in databases, and getting this wrong can leave your primary key index fragmenting as data grows, with write throughput dropping by orders of magnitude.
This guide covers how each version is structured, how they perform, and where each fits.
UUID v4: The Random Default
v4 is by far the most widely used UUID version. Of its 128 bits, 122 are random; the other 6 carry version and variant markers.
// Standard v4 generation
const uuid = crypto.randomUUID();
// "f47ac10b-58cc-4372-a567-0e02b2c3d479"
What v4 gets right:
- Simple: standard library support everywhere,
crypto.randomUUID()ships in all modern browsers and Node.js - No coordination: two independent services generating v4 values will essentially never collide
- No timing leak: the UUID reveals nothing about when it was created
The downsides are mostly database-related, covered next.
UUID v7: The Time-Ordered Standard
v7 was standardized in 2024 by RFC 9562. The layout is fundamentally different:
- 48 bits: Unix millisecond timestamp (around 8900 years of range)
- 4 bits: version marker (7)
- 12 bits: random or monotonic counter (orders within the same millisecond)
- 2 bits: variant marker
- 62 bits: random
// v7 generation (requires a supporting library, e.g. uuid v11+)
import { v7 } from 'uuid';
const id = v7();
// "017f22e2-79b0-7cc3-98c4-dc0c0c07398f"
Notice the leading hex digits are the timestamp. v7 UUIDs sort naturally by creation time.
Why Sortability Matters In Databases
This is the reason v7 exists.
Most databases (PostgreSQL, MySQL, SQL Server) use B-trees for primary key indexes. B-trees perform best with ordered insertion. v4 values are random, so every insert lands somewhere arbitrary in the tree. Over time this causes:
- Page splits: when an insert target is full, the node splits, triggering extra disk IO
- Cache thrashing: random writes blow out the buffer pool as leaf pages get repeatedly evicted
- Fragmentation: the physical layout of the index gets progressively more scattered, hurting range scans
- Clustered index pain: in engines like InnoDB where the primary key dictates physical row order, v4 forces every insert to physically relocate data
v7 fixes this. UUIDs generated in the same time window are contiguous in the B-tree, so writes shift from random to roughly append-only.
The measurable impact is significant. In PostgreSQL tests inserting 10 million rows, v7 is typically 2 to 5x faster than v4, with index sizes 30% smaller or more. The exact numbers depend on hardware and configuration, but the direction is consistent.
Collision Probability: Is v4 Really Safe?
v4 has 122 bits of randomness, which makes collisions vanishingly unlikely but not impossible. The birthday paradox says you'd need around 2^61 (about 2.3 × 10^18) v4 UUIDs before reaching a 50% chance of one collision.
That's far beyond any real system's scale. But for high-throughput services generating millions of IDs per second over years of operation, the cumulative probability is worth thinking about. v7 narrows the collision window to the random portion within a single millisecond, and a monotonic counter drives the probability even lower.
v4 vs v7 Comparison
| Dimension | UUID v4 | UUID v7 |
|-----------|---------|---------|
| Random bits | 122 | 74 (plus timestamp) |
| Time-sortable | ✗ | ✓ |
| DB index friendly | ✗ (random writes) | ✓ (near-sequential) |
| Information leak | No timing exposure | Creation time inferable |
| Standardized | 2005 (RFC 4122) | 2024 (RFC 9562) |
| Library support | Universal | All major libraries |
| Sorted queries | Requires created_at column | ORDER BY id works |
When v4 Is The Right Choice
- Pure stateless systems: log entry IDs, cache keys, ephemeral tokens where ordering doesn't matter
- Mildly security-sensitive contexts: when you don't want IDs to expose creation time (though this "security" is weak, timestamps leak through many other channels)
- Legacy system compatibility: migration to v7 requires evaluating existing code assumptions
- Cross-system coordination: integrating with external systems still on v4
When v7 Wins
- Anything using a database primary key: the core design goal of v7
- Event logs and audit trails: naturally time-sorted, eliminating a separate created_at index
- High-throughput write systems: less IO amplification from B-tree fragmentation
- "Sort by time without a timestamp column" patterns: with caveats about timestamp leakage
Migration Notes
If you're moving from v4 to v7, keep these in mind:
- Storage format is unchanged: both are 128 bits, no schema change needed
- Leave existing rows alone: don't backfill; new rows use v7, old rows stay v4
- Sort semantics shift: queries that ordered by created_at can sometimes order by id instead, but only once all rows are v7
- Rebuild indexes: after migration, rebuild the primary key index once to reorganize the old v4 data
-- PostgreSQL: check primary key index bloat
SELECT pg_size_pretty(pg_relation_size('your_table_pkey'));
-- Rebuild if needed
REINDEX INDEX your_table_pkey;
Generate UUIDs Locally
Whether you need v4 or v7, you can generate them without depending on a server.
The UUID Generator supports bulk UUID generation entirely in the browser. No server round-trips, no logging. Especially useful when generating test fixtures or internal identifiers you don't want exposed to a third-party service.
Quick command-line options:
# Node.js (v4)
node -e "console.log(crypto.randomUUID())"
# Python (v4)
python3 -c "import uuid; print(uuid.uuid4())"
# v7 needs a third-party library
npx uuidv7
The short version: v4 is fine for stateless systems; for anything touching a database or needing chronological order, v7 is the sensible default in 2026. RFC 9562 has been out for two years, library support is solid, and there's no good reason for new projects to keep paying the index fragmentation tax that v4 imposes.
relatedTools
Related Articles
HTTP Status Codes Reference: Quick Lookup Guide
Complete HTTP status code reference. Understand 1xx, 2xx, 3xx, 4xx, 5xx categories, common scenarios, and correct usage in API development.
Cron Expressions Explained: From '* * * * *' to Complex Schedules
Master cron expression syntax with practical examples. Learn the five fields, step values, ranges, and the day-of-month vs day-of-week gotcha.
Online Color Converter: HEX, RGB, HSL Color Format Conversion
Learn how to use an online color converter to switch between HEX, RGB, and HSL formats. Understand color representation fundamentals and best practices for frontend development.