UUID v4 vs v7: which should you use?
Short answer: v7 for database primary keys, v4 when creation time must stay secret. Here is the full reasoning.
By the Withuse team · Updated
The one-table summary
| UUID v4 | UUID v7 | |
|---|---|---|
| Structure | 122 random bits | 48-bit Unix ms timestamp + 74 random bits |
| Sortable by creation time | No | Yes (lexicographic = chronological) |
| B-tree index behavior | Random inserts → page splits, cache misses | Append-mostly → compact, fast |
| Leaks creation time | No | Yes (by design) |
| Standard | RFC 4122 / RFC 9562 | RFC 9562 (2024) |
Why v7 wins for database primary keys
Every insert with a purely random v4 key lands at a random position in the index. At scale this causes constant B-tree page splits, write amplification, and poor cache locality — a well-documented cause of slow inserts in PostgreSQL and MySQL. v7 keys are generated in (roughly) increasing order, so inserts append to the right-hand edge of the index like an auto-increment integer, while remaining globally unique and generatable on any client without coordination.
As a bonus, v7 keys make “recent items first” queries cheap: ordering by the primary key approximates ordering by creation time.
When v4 is still the right choice
- The timestamp is sensitive. A v7 ID reveals when the row was created to anyone who sees the ID. Password-reset tokens, invite codes, or IDs exposed in URLs where creation time could leak business information are better served by v4.
- Unpredictability matters. v4's 122 random bits make guessing adjacent IDs hopeless. v7 narrows the search space for an attacker who knows the approximate creation time (74 random bits is still a lot — but it is a smaller margin).
- Legacy constraints. Some libraries and databases only recognize v4 validation patterns.
Practical recommendation
Default to v7 for anything stored and indexed (primary keys, event IDs, log records) and v4 for anything secret or externally visible where timing shouldn't leak. Both are 128-bit standard UUIDs, so switching costs nothing at the schema level — a uuid column holds either.
Try both right now: generate UUID v7 or UUID v4 in your browser — free and offline.
Frequently asked questions
Can I mix UUID v4 and v7 in the same table?
Yes. Both are 128-bit values that live in the same uuid column type, and the version digit is simply part of the value, so a table can hold a mixture without any schema change or migration. Systems often end up this way naturally: rows created before a switch to v7 keep their v4 identifiers while new rows get v7 ones. What you lose is the guarantee that primary key order approximates insert order, since the old v4 rows scatter through the index while new v7 rows cluster at the end. That is usually acceptable, because the index-locality benefit applies to new writes, which is where the cost was. If you need strict ordering across all rows, sort on a timestamp column instead of the key.
Does UUID v7 work in MySQL and SQL Server?
The values work everywhere; only generation differs. Neither MySQL nor SQL Server ships a native v7 function today, so you generate the identifier in your application and insert it like any other UUID. In MySQL store it as BINARY(16) using UUID_TO_BIN(), and be aware that the swap-flag second argument was designed to reorder version 1 timestamps and should be left off for v7, whose bytes are already in sortable order. In SQL Server use the uniqueidentifier type, but note that its default sort order is not plain byte order, so v7's clustering benefit may not materialise on a clustered index there. PostgreSQL 18 is currently the database with native uuidv7() support.
Is UUID v7 less secure than v4?
It is more predictable, which matters only in specific contexts. A v7 value publishes its creation time in the first 48 bits and keeps 74 random bits, against 122 random bits in v4. Nobody is going to brute-force 74 bits, so v7 remains unguessable for practical purposes, but an attacker who knows roughly when a record was created faces a smaller search space, and anyone holding the identifier learns the timestamp for free. That last property is the real consideration: sequential-looking signup identifiers can reveal growth rates to a competitor, and a password-reset token that discloses its issue time narrows an attack window. Use v4 for secrets and externally visible identifiers where timing is sensitive.
Should I migrate existing v4 primary keys to v7?
Usually not. Rewriting primary keys means rewriting every foreign key that references them, invalidating caches and external links that hold the old identifiers, and taking downtime or running a complex dual-write migration — a lot of risk for a write-performance benefit that only accrues to future rows anyway. The pragmatic path is to switch the default for new inserts and leave existing rows alone, accepting a mixed table. Consider a real migration only when the table is genuinely large enough that insert throughput or index bloat is a measured production problem, and even then benchmark your own workload first rather than relying on published figures from a different schema and hardware.