UUID RFC 9562 Deep Dive: What’s New in UUIDv6, UUIDv7 & UUIDv8
For nearly two decades, RFC 4122 served as the global standard for generating 128-bit Universally Unique Identifiers (UUIDs). While UUID Version 4 became the default choice across distributed architectures, its completely random bit distribution introduced a severe architectural bottleneck: catastrophic B-Tree index fragmentation and write amplification in relational databases. In May 2024, the Internet Engineering Task Force officially published RFC 9562, obsoleting RFC 4122 and standardizing UUIDv6, UUIDv7, and UUIDv8 to deliver time-ordered, database-friendly locality without sacrificing collision resistance.
RFC 9562 is the official IETF standard for Universally Unique Identifiers that obsoletes RFC 4122. It introduces three time-ordered formats—UUIDv6 (reordered Gregorian), UUIDv7 (Unix Epoch millisecond), and UUIDv8 (custom vendor)—engineered to preserve clustered database index performance while maintaining 128-bit binary compatibility with legacy systems.
The Problem with UUIDv4: The Hidden Cost of Pure Randomness
UUIDv4 allocates 122 bits to pure pseudorandomness, reserving 4 bits for the version code (0100 in binary) and 2 bits for the variant flag (10). With 2122 possible permutations, the probability of generating a duplicate ID is low enough to safely neglect across decentralized nodes without coordination.
However, relational database storage engines—such as PostgreSQL (using standard B-Tree secondary indexes and Heap storage) and MySQL InnoDB (using primary clustered B+ Trees)—organize rows sequentially on disk. These engines rely on monotonic (strictly ascending) keys to append incoming data directly to the rightmost leaf node of the tree.
What Happens During UUIDv4 Primary Key Inserts?
- Random Inode Insertion: Because UUIDv4 values are evenly distributed across the hexadecimal spectrum, each new
INSERTtargets an arbitrary page within the B+ Tree. - Cascading Leaf Page Splits: When an 8KB (PostgreSQL) or 16KB (InnoDB) leaf page fills to capacity, writing a new random record forces the storage engine to split the node, moving roughly 50% of the rows to a newly allocated page.
- Buffer Pool Churn: As datasets expand beyond available system memory (RAM), random inserts consistently hit cold leaf pages, forcing continuous I/O cycles to load disk blocks into the database buffer pool.
- Severe Write Amplification: Storing a single 16-byte random identifier can force an entire 16KB dirty page to be flushed to non-volatile storage, accelerating SSD write endurance wear.
| Metric (10,000,000 Inserts) | Auto-Increment (BIGINT) | UUIDv4 (Random) | UUIDv7 (RFC 9562) |
|---|---|---|---|
| Index Size on Disk | ~220 MB | ~680 MB (+209%) | ~310 MB (+40%) |
| Insert Throughput (rows/sec) | ~48,000 | ~7,200 (-85%) | ~43,500 (-9%) |
| B-Tree Page Fragmentation | < 2% | > 45% | < 3% |
RFC 9562: Structural Overview of UUIDv6, UUIDv7, and UUIDv8
Recognizing that production systems were turning to non-standard, fragmented solutions like ULID, KSUID, and Snowflake, the IETF ratified RFC 9562. This standard establishes three new time-ordered formats while preserving the exact 128-bit structure and string layout (8-4-4-4-12) required by existing database drivers.
| UUID Version | Timestamp Source | Sort Order | Random Entropy | Primary Use Case |
|---|---|---|---|---|
| UUIDv1 (Legacy) | 60-bit Gregorian (100ns) | Non-monotonic (reversed) | None (Clock Seq + MAC) | Legacy networking |
| UUIDv4 (Legacy) | None | Completely random | 122 bits | Ephemeral tokens, non-indexed IDs |
| UUIDv6 (RFC 9562) | 60-bit Gregorian (100ns) | Monotonic (big-endian) | Clock Seq + Node ID | Direct migration from UUIDv1 |
| UUIDv7 (RFC 9562) | 48-bit Unix Epoch (ms) | Monotonic (chronological) | 62 to 74 bits CSPRNG | Modern database primary keys |
| UUIDv8 (RFC 9562) | Custom / Vendor defined | Custom dependent | Variable (up to 122 bits) | Specialized/enterprise formats |
1. UUID Version 6: Backward Compatibility with Gregorian Time
UUIDv6 is a direct fix for UUIDv1 systems. UUIDv1 tracked time using a 60-bit count of 100-nanosecond intervals since October 15, 1582, but arranged these bits in reverse order (time_low, time_mid, time_high_and_version).
UUIDv6 takes those exact 60 timestamp bits and reorders them into standard big-endian byte order (most significant bits first). This guarantees chronological, natural sorting while retaining compatibility with applications designed to extract legacy Gregorian timestamps.
2. UUID Version 7: The Default for Modern Web Architecture
UUIDv7 is the recommended standard for distributed systems, microservices, and database primary keys. It combines a 48-bit Unix Epoch millisecond timestamp with a 4-bit version field (0111), a 2-bit variant (10), and up to 74 bits of pseudorandom data.
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 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | unix_ts_ms | ver | rand_a | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |var| rand_b | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | rand_b | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
UUIDv7 Bit Structure:
unix_ts_ms(48 Bits): An unsigned big-endian integer measuring milliseconds since the Unix Epoch (1970-01-01 00:00:00 UTC). A 48-bit millisecond counter will not roll over until August 2, 10889 AD, fully resolving the 32-bit Year 2038 problem.ver(4 Bits): The binary sequence0111(hexadecimal0x7), designating UUID Version 7.rand_a(12 Bits): Cryptographically generated random bits, or an optional sub-millisecond sequence counter to ensure strict monotonicity on high-throughput nodes.var(2 Bits): The binary sequence10, certifying compliance with RFC 4122 and RFC 9562 variants.rand_b(62 Bits): High-entropy cryptographic pseudorandom bits generated by the host system's CSPRNG.
3. UUID Version 8: Custom and Vendor Architectures
RFC 9562 formalizes UUIDv8 for specialized enterprise requirements. Beyond the mandatory 4-bit version (1000) and 2-bit variant (10), the remaining 122 bits are entirely unconstrained, giving teams a standardized container for custom formats (such as nanosecond timestamps combined with server cluster IDs).
Collision Resistance: The Math of Concurrency Spikes
Because UUIDv7 groups IDs by timestamp, developers often ask: What happens when thousands of records are inserted within the same millisecond?
When sub-millisecond counters are omitted, UUIDv7 provides 74 bits of pure randomness (12 bits in rand_a + 62 bits in rand_b). Using the mathematical approximation for the Birthday Problem, the number of generated IDs (N) required to reach a collision probability (p) of 1 × 10−9 (one in a billion) across an entropy pool of k = 74 bits is:
An application must generate more than 194,000 unique UUIDs within a single millisecond on a single node before reaching a one-in-a-billion collision chance. For distributed architectures, UUIDv7 delivers reliable uniqueness paired with native B-Tree sortability.
UUIDv7 vs. ULID: A Direct Comparison
Before RFC 9562 was finalized, ULID (Universally Unique Lexicographically Sortable Identifier) emerged as a popular community-driven workaround for index fragmentation.
- Standardization & Longevity: ULID is an open-source community specification with varying implementation standards across languages. UUIDv7 is an officially ratified standard published by the IETF in RFC 9562.
- Database Storage Compatibility: ULID uses Crockford’s Base32 string format (26 characters). In relational databases, storing ULIDs requires either custom text columns (costing 26 bytes) or manual byte conversions. UUIDv7 maps directly to native 16-byte
UUIDtypes in PostgreSQL, SQLite, and CockroachDB without conversion overhead. - Entropy Placement: ULID allocates 80 bits to randomness after a 48-bit timestamp; UUIDv7 reserves 6 bits for official versioning and variant flags, leaving 74 bits for entropy.
Database Implementation Guide
PostgreSQL Implementation
PostgreSQL natively supports 16-byte UUID data types. In PostgreSQL 17+, you can generate UUIDv7 values using built-in functions, or install the popular community extension pg_uuidv7 for older releases:
-- Example schema leveraging time-ordered UUIDv7 in PostgreSQL
CREATE TABLE customer_orders (
order_id UUID PRIMARY KEY,
customer_email VARCHAR(255) NOT NULL,
order_total NUMERIC(10, 2) NOT NULL,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
-- Primary key B-Tree index inserts append sequentially to the rightmost leaf node.
-- Page splits drop toward zero; write speeds remain stable over millions of rows.
MySQL 8+ Implementation
MySQL InnoDB stores primary keys in a clustered index. While UUIDv1 required byte-swapping with UUID_TO_BIN(v1, 1) to avoid page splits, UUIDv7 places the big-endian timestamp in the first 48 bits, allowing direct insertion:
-- Optimized MySQL 8 schema using raw 16-byte binary representations
CREATE TABLE payments (
payment_id BINARY(16) NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
PRIMARY KEY (payment_id)
) ENGINE=InnoDB;
-- Insert UUIDv7 without byte swapping (flag 0)
INSERT INTO payments (payment_id, amount)
VALUES (UUID_TO_BIN('018e69d7-6288-7294-819a-25ec1d31cb76', 0), 149.50);
TypeScript & Node.js Implementation
You can generate RFC 9562-compliant UUIDv7 strings natively using the Web Crypto API:
import { webcrypto } from 'node:crypto';
export function generateUUIDv7(): string {
const bytes = new Uint8Array(16);
webcrypto.getRandomValues(bytes);
const timestamp = Date.now();
// 48-bit Big-Endian Unix Epoch Millisecond Timestamp
bytes[0] = (timestamp / 0x10000000000) & 0xff;
bytes[1] = (timestamp / 0x100000000) & 0xff;
bytes[2] = (timestamp / 0x1000000) & 0xff;
bytes[3] = (timestamp / 0x10000) & 0xff;
bytes[4] = (timestamp / 0x100) & 0xff;
bytes[5] = timestamp & 0xff;
// Set Version 7 (0111) in octet 6
bytes[6] = (bytes[6] & 0x0f) | 0x70;
// Set Variant 10 (RFC 4122/9562) in octet 8
bytes[8] = (bytes[8] & 0x3f) | 0x80;
return [...bytes].map((b, i) =>
([4, 6, 8, 10].includes(i) ? '-' : '') + b.toString(16).padStart(2, '0')
).join('');
}
Zero-Downtime Migration: Transitioning from UUIDv4 to UUIDv7
Because UUIDv7 shares the same 128-bit structure, 36-character hyphenated representation, and RFC variant flags as legacy formats, migrating existing production tables requires zero downtime:
- Keep Schema Definitions Unchanged: Retain existing
UUID,BINARY(16), orVARCHAR(36)columns. No database alterations or table locks are required. - Deploy Application-Layer Generation: Configure background workers and API services to emit UUIDv7 for all newly created entities. Existing historical UUIDv4 records remain untouched.
- Observe Immediate Index Stabilization: Because newly inserted keys are monotonically increasing, new records append cleanly to the right side of the B-Tree, and write throughput stabilizes immediately.
Security & Privacy Considerations: Timestamp Leakage
While UUIDv7 solves the indexing problem, it is important to remember that it is time-ordered, not opaque. Because the first 48 bits encode the creation millisecond:
- User Activity Tracking: Exposing UUIDv7 values in public URLs or public REST APIs lets observers determine account registration dates, resource creation times, and transactional frequency.
- Architectural Solution: Use UUIDv7 as an internal primary key for database indexing, and generate a separate, completely random UUIDv4 or NanoID as a public-facing resource token.
Frequently Asked Questions (FAQ)
What is RFC 9562?
RFC 9562 is the updated IETF standard for Universally Unique Identifiers (UUIDs) published in 2024, officially obsoleting RFC 4122. It introduces three new time-ordered UUID versions—UUIDv6, UUIDv7, and UUIDv8—engineered to eliminate database B-Tree index fragmentation while retaining 128-bit binary compatibility.
Why is UUIDv7 better for database primary keys than UUIDv4?
UUIDv4 is completely random, causing arbitrary page splits in B-Tree indexes, dropping insert throughput by up to 85% and increasing disk fragmentation. UUIDv7 prefixes a 48-bit millisecond Unix timestamp, allowing records to append sequentially to the rightmost leaf node of the index.
Can I store UUIDv7 in an existing standard UUID database column?
Yes. UUIDv7 uses the exact same 128-bit memory representation, 36-character hyphenated string format, and RFC variant flags as legacy UUIDs. It works natively in existing PostgreSQL, MySQL, and SQLite UUID columns with zero schema changes.
Does UUIDv7 suffer from the Year 2038 timestamp problem?
No. UUIDv7 allocates 48 bits to its Unix millisecond timestamp. This 48-bit unsigned counter will not overflow until August 2, 10889 AD, making it immune to the 32-bit Year 2038 signed integer rollover.
Does UUIDv7 leak sensitive information?
Unlike UUIDv1, which leaked hardware MAC addresses, UUIDv7 contains only a public Unix Epoch millisecond timestamp and cryptographically secure pseudorandom bits. While it exposes the creation time, it reveals no host machine or network identity details.