UUID v4 vs v7: Which UUID Should You Use as a Database Key?
How UUID v4 and v7 are built, why random v4 keys slow down database indexes, what v7's timestamp reveals, and how to choose between UUIDs and integer IDs.
By Avinash KoriPublished 5 min read
On this page
UUIDs are the default answer to "how do I generate an ID without asking the database?" They can be created anywhere (in a browser, on a phone, in ten services at once) without coordination and without collisions. For years the answer was UUID version 4, the random one. Since RFC 9562 was published in 2024, there's a better default for most databases: version 7, which puts a timestamp at the front.
This guide explains how both versions are built, why the difference matters for database performance, and when you shouldn't use a UUID at all.
What a UUID is
A UUID (universally unique identifier, called GUID in the Microsoft world) is a 128-bit number, usually written as 32 hex digits in five groups:
be349389-b70c-4cda-a44e-653779084521
^ ^
| variant (8, 9, a or b)
versionThe first digit of the third group is the version: 4 here. The first digit of the fourth group encodes the variant, which for standard UUIDs is always 8, 9, a or b. Everything else depends on the version.
UUID v4: 122 random bits
Version 4 is simple. Apart from the 6 bits used by the version and variant, all 122 bits are random. In JavaScript it's one call:
crypto.randomUUID(); // "be349389-b70c-4cda-a44e-653779084521"Python has uuid.uuid4(), PostgreSQL has gen_random_uuid(), and the UUID Generator creates them in bulk.
Will two ever collide? With 122 random bits, you'd need to generate about 2.7 × 10¹⁸ UUIDs (2.7 quintillion) before there's a 50% chance of a single duplicate. At a billion per second, that takes 86 years. In practice, collisions come from bugs, not chance: a weak random generator, a cloned VM that reuses its random state, or code that copies an ID instead of generating a new one. Always generate UUIDs with a cryptographically secure source, as crypto.randomUUID() does.
The problem with random keys
Randomness is exactly what makes v4 awkward as a database primary key. Most databases store primary keys in a B-tree index (and MySQL's InnoDB stores the whole table ordered by primary key). With auto-incrementing integers, every new row goes at the end of the index. With random UUIDs, every new row lands at a random position:
- Inserts touch random pages. Instead of appending to the last page of the index, which stays in memory, each insert may need a different page read from disk.
- Page splits and fragmentation. Inserting into the middle of full pages splits them, leaving the index larger and half-empty.
- Poor cache use. Recent rows, which are queried most, are scattered across the whole index instead of being close together.
On small tables you won't notice. On tables with tens of millions of rows and high insert rates, random keys can make inserts several times slower and indexes noticeably larger than sequential keys.
UUID v7: time first, then random
Version 7 fixes this by putting a timestamp at the front. Its layout is:
| Bits | Content |
|---|---|
| 48 | Unix timestamp in milliseconds |
| 4 | Version (7) |
| 12 | Random (or a sub-millisecond counter) |
| 2 | Variant |
| 62 | Random |
Because the most significant bits are the time, UUIDs generated later sort after UUIDs generated earlier. New rows go near the end of the index, just like auto-increment integers, while the 74 random bits keep IDs unique across machines without coordination.
Here's a real v7 UUID and the timestamp inside it:
const id = '01a13f6a-9d9e-775c-a738-35ed785be40d';
const created = new Date(parseInt(id.replace(/-/g, '').slice(0, 12), 16));
created.toISOString(); // "2026-10-15T11:55:14.206Z"The first 12 hex digits (01a13f6a9d9e) are the millisecond timestamp. You can paste that number, converted to decimal, into the Unix Timestamp Converter to check it. This is handy for debugging: you can tell roughly when a record was created from its ID alone.
Generating v7 without a library is short too:
function uuidv7() {
const bytes = crypto.getRandomValues(new Uint8Array(16));
const ms = BigInt(Date.now());
for (let i = 0; i < 6; i++) bytes[i] = Number((ms >> BigInt(8 * (5 - i))) & 0xffn);
bytes[6] = (bytes[6] & 0x0f) | 0x70; // version 7
bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10xx
const hex = [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('');
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
}This simple version doesn't guarantee order within the same millisecond; libraries that follow RFC 9562's optional counter method do. Support is spreading quickly: PostgreSQL 18 added a built-in uuidv7() function, Python 3.14 added uuid.uuid7() to the standard library, and libraries exist for every major language (the uuid package on npm, github.com/google/uuid in Go).
The trade-off: v7 reveals when it was created
The timestamp that makes v7 fast is also information. Anyone who sees a v7 ID knows when the record was created, to the millisecond. Usually that's harmless (creation dates are often visible anyway), but think about it for:
- User or account IDs, where the signup date might be sensitive.
- IDs in URLs that could reveal business volume. Sequential timestamps let an outsider estimate how many orders you take per hour by sampling IDs over time.
Neither v4 nor v7 should ever be used as a secret, such as a password-reset token or an API key. UUIDs are identifiers: they end up in logs, URLs and analytics. Secrets should come from a dedicated secure generator, like the Random String Generator, with 32 bytes of randomness and nothing predictable in them. Our guide to password entropy explains why.
UUIDs vs integer IDs
UUIDs aren't always the right choice. Compare them with auto-increment integers:
| Auto-increment integer | UUID v4 | UUID v7 | |
|---|---|---|---|
| Size | 4–8 bytes | 16 bytes | 16 bytes |
| Generated without the database | No | Yes | Yes |
| Index-friendly inserts | Yes | No | Yes |
| Reveals creation order/count | Yes (count and order) | No | Creation time |
| Safe to merge data from many sources | No | Yes | Yes |
Integers are smaller and simpler, and fine for a single database where IDs never leave your system. UUIDs win when clients create records offline, when several services or databases generate IDs, when you merge datasets, or when you don't want IDs to reveal how many rows exist (/invoices/1043 invites someone to try /invoices/1042, though real access control is what must stop them).
Storage tips
- Store UUIDs as binary, not text. A UUID in a
CHAR(36)column takes 36 bytes plus overhead; asBINARY(16)(MySQL) or the nativeuuidtype (PostgreSQL) it takes 16. Indexes shrink by more than half. - In MySQL, mind the byte order.
UUID_TO_BIN(uuid, 1)swaps the time fields of v1 UUIDs for better ordering; don't apply that swap to v7, which is already ordered. - Keep one format in APIs. Lower-case with hyphens is the canonical text form. The UUID Generator can produce upper-case, hyphen-less or braced variants when another system expects them.
Which should you choose?
- New database primary keys: UUID v7.
- IDs that must not reveal creation time: UUID v4.
- Single database, internal IDs only: an auto-increment integer is still perfectly fine.
- Secrets and tokens: neither; use 32 bytes from a secure random generator.
Summary
UUID v4 is 122 random bits: unique and opaque, but random keys fragment database indexes. UUID v7 starts with a 48-bit millisecond timestamp, so new IDs sort in creation order and insert like sequential integers while staying unique across machines. Use v7 for database keys, v4 when the creation time must stay private, store either as 16 bytes, and never use a UUID as a secret.

Written by
Avinash Kori
Founder of Devtoolry
Avinash is a full-stack web developer from Surat, India, with eight years of experience in PHP, Laravel and Angular. He built Devtoolry so developers can solve everyday problems quickly with free tools that run entirely in the browser, and writes practical guides on the formats, encodings and techniques behind them.
Try it yourself
The free tools used in this article. They run in your browser, so nothing you paste is uploaded.
UUID Generator
Generate UUID v4 and time-ordered UUID v7 identifiers (GUIDs) in bulk, with uppercase, hyphen and brace options, using a cryptographically secure generator.
Unix Timestamp Converter
Convert a Unix timestamp (epoch time) in seconds or milliseconds to a readable date and back, in any time zone, and see the current Unix time live.
Random String Generator
Generate random strings, tokens and API keys online in hex, Base64URL or a custom alphabet, using your browser's cryptographically secure generator.