UUID v4 vs v7: choosing IDs for your database
By Uxair Ijaz · 2026-10-24 · 7 min read
A UUID (universally unique identifier, called a GUID in the Microsoft world) is a 128-bit value that any computer can generate on its own, with no central counter, and still be practically certain nobody else will ever produce the same one. That makes UUIDs ideal for distributed systems, offline apps, public URLs and merging data from several databases.
There are several versions, and the choice matters more than it looks, especially for database primary keys. This guide explains how to read a UUID, what each version contains, why v7 is now the best default for most IDs, and when v4 is still the right call. Generate and inspect UUIDs as you read with the UUID Generator.
Reading a UUID
018f3c6e-8a4b-7c2d-9e1f-5a6b7c8d9e0f
^ ^
version variant
A UUID is 32 hexadecimal digits in five groups: 8-4-4-4-12. Two positions identify it:
- The version is the first digit of the third group. Here
7c2dstarts with7, so this is a version 7 UUID. - The variant is the first digit of the fourth group. For standard UUIDs it is
8,9,aorb.
The rest are the payload bits: random data, a timestamp, or both. The current standard is RFC 9562 (2024), which replaced RFC 4122 and added versions 6, 7 and 8.
The versions
| Version | Contents | Sortable by time | Reveals | Use |
|---|---|---|---|---|
| v1 | 60-bit timestamp + clock sequence + node (often a MAC address) | No (fields are out of order) | Creation time, machine identity | Legacy only |
| v3 / v5 | MD5 / SHA-1 hash of a namespace and a name | No | Nothing extra | Deterministic ids from names |
| v4 | 122 random bits | No | Nothing | Unpredictable ids |
| v6 | v1 with the timestamp reordered | Yes | Creation time, machine identity | Migrating from v1 |
| v7 | 48-bit Unix time in ms + 74 random bits | Yes | Creation time (to the millisecond) | Database keys, logs, events |
| v8 | Custom layout | Up to you | Up to you | Experimental formats |
| Nil / Max | All zeros / all f |
Placeholders and sentinels |
In practice, nearly every choice comes down to v4 or v7.
UUID v4: pure randomness
A v4 UUID is 122 random bits plus the 6 fixed version and variant bits. In browsers and Node.js, crypto.randomUUID() produces one from a cryptographically secure generator.
How unique is "unique"? With 2¹²² possible values, you would need to generate about 2.7 × 10¹⁸ UUIDs, roughly a billion every second for 85 years, before the chance of even one collision reached 50%. Your database will fail for other reasons long before that.
v4's strength is that it reveals nothing: not when it was created, not where, not how many came before it. Its weakness shows up in databases.
Why random keys hurt databases
Most databases store a primary key in a B-tree index, which keeps keys in sorted order. Auto-increment integers always arrive at the right-hand end of the tree, so inserts touch the same few pages, which stay in memory.
Random v4 keys land anywhere in the tree. As the table grows:
- each insert touches a different, often uncached page, so the database reads from disk;
- pages split constantly and end up half-empty, so the index grows larger than it needs to be;
- in databases that store rows in primary-key order (MySQL InnoDB, SQL Server clustered indexes), the table itself gets fragmented the same way.
On small tables none of this matters. On tables with tens of millions of rows and heavy write load, random keys can make inserts several times slower and indexes noticeably larger.
UUID v7: time first, then random
A v7 UUID starts with a 48-bit Unix timestamp in milliseconds, followed by 74 random bits:
018f3c6e-8a4b-7c2d-9e1f-5a6b7c8d9e0f
└──────────┘
Unix time in ms → 2024-05-03T03:10:28.427Z (decoded by the inspector)
Because the time comes first, v7 UUIDs sort in creation order, as text and as bytes. New keys arrive at the end of the index, just like an auto-increment integer, so inserts stay fast and indexes stay compact. You also get useful side effects:
ORDER BY idreturns rows roughly in creation order;- you can tell when a record was created from its id alone;
- ids from different servers merge into one sensibly ordered stream.
Inside a single millisecond the order of ids depends on the generator: some use a counter in the random bits to guarantee order within the same millisecond; others are random within it. The 74 random bits still make collisions practically impossible.
Paste a v7 UUID into the inspector in the UUID Generator and it shows the embedded timestamp.
Choosing between v4 and v7
Use v7 for:
- database primary keys and foreign keys;
- event, log and message ids that you sort or page through by time;
- anything you would otherwise use an auto-increment integer for, but need to generate on many machines or before inserting.
Use v4 when the creation time must stay hidden:
- ids exposed to users where timing could leak information, such as when an account was created or how fast orders arrive;
- identifiers that should give no hint about related records.
Use neither as a secret. UUIDs identify things; they are not meant to prove that someone is allowed to see them. Password-reset links, API keys and session tokens need at least 128 bits from a secure random generator, in a format designed for the job. A v7 UUID has only 74 random bits, and part of it can be guessed from the time.
Storing UUIDs
A UUID is 16 bytes. As text with hyphens, it is 36 characters, more than twice the size, and slower to compare.
| Database | Recommended type |
|---|---|
| PostgreSQL | uuid (16 bytes). PostgreSQL 18 adds a built-in uuidv7() function |
| MySQL / MariaDB | BINARY(16). MySQL's UUID_TO_BIN(id, 1) reorders v1 for better locality, but do not use it with v7, which is already in order. MariaDB 10.7+ has a native UUID type |
| SQL Server | uniqueidentifier. SQL Server compares the last six bytes first, so v7 ids do not arrive in index order there; its NEWSEQUENTIALID() serves that purpose instead |
| SQLite | BLOB (16 bytes), or TEXT if readability matters more than size |
In application code and JSON, the standard lowercase hyphenated form is the safest choice. The generator's options cover the other forms you meet: Uppercase, No hyphens, {Braces} (common in Windows registry files and C#) and "Quoted", for pasting straight into code or SQL.
Generating UUIDs in code
// v4: built into every modern browser and Node.js
const id = crypto.randomUUID();
// v7: 48-bit millisecond timestamp, then random bits with the version and variant set
function uuidv7(): string {
const b = crypto.getRandomValues(new Uint8Array(16));
const ms = Date.now();
for (let i = 0; i < 6; i++) b[i] = Math.floor(ms / 2 ** (8 * (5 - i))) & 0xff;
b[6] = (b[6] & 0x0f) | 0x70; // version 7
b[8] = (b[8] & 0x3f) | 0x80; // RFC 9562 variant
const h = Array.from(b, (x) => x.toString(16).padStart(2, '0')).join('');
return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
}
For production, prefer your language's standard library or a well-maintained package, ideally one that keeps ids in order within the same millisecond.
UUID alternatives
- ULID: 128 bits like v7, time-ordered, written as 26 Base32 characters. Popular before v7 existed; v7 now gives the same benefits in the standard UUID format that databases understand natively.
- Snowflake ids: 64-bit time-ordered integers (Twitter, Discord). Smaller, but they need a coordinated worker id per machine.
- Nano ID: short random strings for URLs. Compact, but not a UUID and not time-ordered.
Mistakes to avoid
- Random v4 primary keys on large, write-heavy tables, where v7 would do.
- Storing UUIDs as
VARCHAR(36)when a native or 16-byte binary type exists. - Using UUIDs as secret tokens.
- Exposing v7 ids where the creation time is sensitive.
- Generating ids with
Math.random(), which is not a secure or well-distributed source. Usecrypto.randomUUID()orcrypto.getRandomValues(). - Mixing case or formats across systems, so the same id fails to match. Normalise to lowercase.
Try it
Open the UUID Generator:
- Choose v7 (time-ordered), set a count and generate. Notice that the first characters barely change: that is the timestamp.
- Switch to v4 (random) and compare: every id is completely different.
- Paste any UUID into Inspect a UUID to see its version, variant and, for v1 and v7, the time it was created.