UUID v4 vs v7: What Changed and Why It Matters
Source: Dev.to
📌 UUID v4 – Pure Randomness
f47ac10b-58cc-4372-a567-0d8b62691e10
^^^^
version 4
How it works
- Generate 128 random bits.
- Set the version (4 bits →
0100). - Set the variant (2 bits →
10). - Format as an
8‑4‑4‑4‑12hex string.
The good
- Dead simple to generate.
- Statistically unique (2¹²² possible values).
- No coordination needed between systems.
- No information leakage (no timestamps, no MAC addresses).
The not‑so‑good
- Completely random → terrible for database indexing.
- No natural ordering.
- Cannot extract any useful metadata.
- Causes B‑tree index fragmentation in databases.
UUID v4 has served us well, but “good enough” isn’t the same as “optimal.”
🚀 UUID v7 – Time‑Ordered, Database‑Friendly
019526de-a3c0-7cc0-b2e8-4a1b3c5d7e9f
^^^^^^^^ ^^^^
timestamp version 7
How it works
- Take the current Unix timestamp in milliseconds (48 bits).
- Set the version (4 bits →
0111). - Set the variant (2 bits →
10). - Fill the remaining 62 bits with random data.
- Format as a standard UUID string.
Structure
| Bits | Meaning |
|---|---|
| 48 | Timestamp (ms) |
| 4 | Version (7) |
| 12 | Random a |
| 2 | Variant (10) |
| 62 | Random b |
The good
- Naturally time‑ordered – lexicographic sort = chronological sort.
- Database‑friendly: sequential inserts → minimal B‑tree page splits.
- Timestamp extractable – you can read when the UUID was created.
- Still globally unique (62 random bits + millisecond timestamp).
- Drop‑in replacement: same 128‑bit size, same string format.
The trade‑offs
- Leaks creation time (first 48 bits are the timestamp).
- Slightly less randomness than v4 (62 vs 122 random bits).
- Newer – not all libraries support it yet.
📈 Why Ordering Matters for Primary Keys
When you use UUID v4 as a primary key in a B‑tree indexed table (the default for most tables), every INSERT goes to a random location in the index, causing:
- Page splits – constant B‑tree re‑organization.
- Cache misses – no locality of reference.
- Write amplification – more I/O per insert.
- Index bloat – fragmented pages with wasted space.
At scale, this hurts performance.
UUID v7 fixes that
Because UUID v7 values are time‑ordered, new inserts always go to the end of the B‑tree:
- Sequential writes – appending, not random inserting.
- Better cache utilization – recent data is co‑located.
- Fewer page splits – the tree grows naturally.
- Smaller indexes – less fragmentation.
Benchmarks consistently show 2‑10× better INSERT performance with UUID v7 vs v4 on PostgreSQL and MySQL, depending on table size and workload.
✅ When to Use Which Version
| Scenario | Recommendation |
|---|---|
| Database primary key | v7 |
| API request tracing | v7 |
| Session tokens | v4 |
| Event sourcing | v7 |
| Password‑reset tokens | v4 |
| Distributed log entries | v7 |
| Cache keys (no ordering needed) | v4 |
Decision matrix (quick glance)
| Need | Use v4 | Use v7 |
|---|---|---|
| Zero information leakage (timestamps are sensitive) | ✅ | ❌ |
| Maximum entropy for secrets/tokens | ✅ | ❌ |
| No ordering required (in‑memory lookups, hash maps) | ✅ | ❌ |
| Only v4 supported by the system | ✅ | ❌ |
| IDs are DB primary keys (main use case) | ❌ | ✅ |
| Want natural chronological ordering | ❌ | ✅ |
| Need to extract creation time from the ID | ❌ | ✅ |
| Distributed systems needing time‑ordered events | ❌ | ✅ |
| Care about DB performance at scale | ❌ | ✅ |
🛠️ How to Generate UUID v7
Node.js (v20+)
import { randomUUID } from 'crypto';
// v4 only (as of Node 22)
const idV4 = randomUUID();
For v7, use a library:
import { v7 as uuidv7 } from 'uuid';
const idV7 = uuidv7();
Python
import uuid
# v4
uuid.uuid4()
# v7 (Python 3.14+ or uuid7 package)
from uuid_extensions import uuid7
uuid7()
PostgreSQL
-- v4 (built‑in)
SELECT gen_random_uuid();
-- v7 (PostgreSQL 17+ or pg_uuidv7 extension)
SELECT uuid_generate_v7();
Online
createuuid.com – generates v1, v4, and v7 instantly in your browser, with bulk‑generation support.
🕵️ Extracting the Timestamp from a UUID v7
function extractTimestamp(uuidV7) {
const hex = uuidV7.replace(/-/g, '');
const timestampHex = hex.substring(0, 12); // first 48 bits = 12 hex chars
const timestampMs = parseInt(timestampHex, 16);
return new Date(timestampMs);
}
// Example
extractTimestamp('019526de-a3c0-7cc0-b2e8-4a1b3c5d7e9f');
// → 2025‑02‑05T…
This is incredibly useful for debugging, auditing, and understanding data flow without extra columns or metadata.
🤔 Should I Migrate Existing v4 Columns to v7?
Short answer: Probably not.
Existing v4 UUIDs work fine. The cost of migrating primary keys in production databases is almost never worth it. Instead:
- Use v7 for new tables and services.
- Keep v4 where it already exists.
- Let the natural lifecycle of your system handle the transition.
Exception: If you’re experiencing real performance issues from UUID v4 index fragmentation at scale—and you’ve confirmed UUIDs are the bottleneck—a planned migration may be justified.
📊 Quick Comparison
| Feature | UUID v4 | UUID v7 |
|---|---|---|
| Introduced | RFC 4122 (2005) | RFC 9562 (2024) |
| Random bits | 122 | 62 |
| Time‑ordered? | ❌ | ✅ |
| DB performance at scale | Poor | Excellent |
| Timestamp readable? | ❌ | ✅ |
| Best for | Tokens, secrets | Primary keys, events |
🎯 Bottom Line
- UUID v7 isn’t a replacement for v4 – it’s the right tool for a different (and very common) job.
- If you’re starting a new project and your UUIDs will be database primary keys, v7 is the better default.
- For tokens, secrets, or any case where ordering isn’t needed, stick with v4.
Need UUIDs right now?
Try createuuid.com – free, instant, no signup.
This article is part of the Developer Tools Deep Dives series, where we explain the “why” behind the tools you use every day.