๐Ÿ” encrypt.tools
IDENTIFIERS ยท RFC 9562

UUID v4 vs UUID v7: Which Should You Use for Database Keys?

UUIDv7 sorts by time and keeps database indexes fast; UUIDv4 is fully random. Compare their structure, collision odds, performance and privacy trade-offs, with code for Postgres, JavaScript and Python.

By ยทยท5 min read

Key takeaways

  • UUIDv7 starts with a 48-bit Unix timestamp in milliseconds, so new IDs sort in creation order. Use it for database primary keys: inserts stay at the end of the index instead of scattering across it.
  • UUIDv4 is 122 bits of pure randomness. Use it when an ID must reveal nothing, including when it was created.
  • Both are defined in RFC 9562 (May 2024), which replaced RFC 4122.
  • Collisions are not a practical concern for either: you'd need about 2.7 ร— 10ยนโธ v4 UUIDs for a 50% chance of one duplicate.
  • Neither is a security token. For session IDs, password-reset links or API keys, use a dedicated random token of 128+ bits.

UUIDs (also called GUIDs) are 128-bit identifiers you can generate anywhere โ€” on a client, in a worker, on a disconnected device โ€” without coordinating with a central database. For years the default answer was version 4: just random bits. RFC 9562, published in 2024, standardized version 7, and it has quickly become the recommended choice for database keys. Here's why, and when v4 is still right.

How the bits are laid out

UUIDv4:  xxxxxxxx-xxxx-4xxx-Yxxx-xxxxxxxxxxxx
         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 122 random bits; "4" = version, Y โˆˆ {8,9,a,b} = variant

UUIDv7:  tttttttt-tttt-7xxx-Yxxx-xxxxxxxxxxxx
         โ””โ”€ 48-bit Unix time (ms) โ”€โ”˜ โ””โ”€โ”€ 74 random bits (+ version/variant)

Because the timestamp sits in the most significant bits, UUIDv7 values compare in time order whether you sort them as strings, bytes or native UUIDs. You can read the creation time straight out of one:

01a16d3e-e1c0-7c41-9f3d-2b6e8c41a5d0
โ”” first 12 hex digits = 0x01a16d3ee1c0 = 1792834200000 ms
                      = 2026-10-24 09:30:00.000 UTC

Side by side

UUIDv4 vs UUIDv7
UUIDv4UUIDv7
Random bits12274 (or fewer with a sub-ms counter)
Sortable by creation timeNoYes (millisecond precision)
Database index localityPoor โ€” inserts land anywhereGood โ€” inserts append
Leaks creation timeNoYes
Native generationEvery platform (crypto.randomUUID())Growing (Postgres 18, Python 3.14, uuid npm)
Best forOpaque public identifiersPrimary keys, event IDs, logs

Why v7 is faster as a primary key

Most databases store primary keys in a B-tree index, kept in sorted order. With random v4 keys, each insert lands at a random position in the tree. Once the index is larger than memory, that means:

  • Random I/O โ€” each insert may need a different index page read from disk.
  • Page splits โ€” full pages split in the middle, leaving pages half empty and the index bloated.
  • Poor cache use โ€” the "hot" set of pages is the entire index.
  • Write amplification โ€” in Postgres, more pages touched means more full-page images in the write-ahead log.

With v7, new keys are always larger than recent ones, so inserts go to the right edge of the index โ€” the same access pattern as an auto-increment integer. Pages fill up completely, the working set stays small, and recently created rows (usually the most queried) sit near each other. Benchmarks routinely show substantially faster bulk inserts and smaller indexes compared with v4 once tables grow beyond memory.

You also get a free, coarse created-at ordering: ORDER BY id returns rows roughly by creation time, and keyset pagination (WHERE id > $last ORDER BY id LIMIT 50) works naturally.

Will they collide?

For v4, the birthday bound puts a 50% chance of a single duplicate at about 2.7 ร— 10ยนโธ IDs โ€” generating a billion per second, that takes 86 years. For v7, the 74 random bits only need to be unique among IDs created in the same millisecond; a 50% collision chance needs about 1.6 ร— 10ยนยน IDs in one millisecond. Neither will happen in practice, provided the generator uses a cryptographically secure random source. (Most real-world UUID collisions trace back to bad randomness โ€” cloned VMs or seeded PRNGs โ€” not to the math.)

When to stick with v4

  • Public identifiers where timing is sensitive. A v7 ID in a URL reveals exactly when a user signed up, an order was placed or a document was created. That may leak business volume or personal information.
  • IDs that must not be correlated. Sequential timestamps make it easier to link records created together.
  • Systems that don't benefit from ordering โ€” small tables, hash-indexed stores, or identifiers that are never indexed.

A common hybrid: use UUIDv7 as the internal primary key, and expose a separate random identifier (v4 or a short random slug) in public URLs.

Generating UUIDv7 in practice

-- PostgreSQL 18+
CREATE TABLE orders (
  id uuid PRIMARY KEY DEFAULT uuidv7(),
  created_at timestamptz NOT NULL DEFAULT now()
);

-- Extract the embedded timestamp
SELECT uuid_extract_timestamp(id) FROM orders LIMIT 1;
// Browsers & Node have v4 built in:
crypto.randomUUID();            // e.g. "3b241101-e2bb-4255-8caf-4136c566a962"

// For v7, use the 'uuid' package (v10+):
import { v7 as uuidv7 } from "uuid";
uuidv7();                        // e.g. "019a1a3c-7b2e-7c41-9f3d-2b6e8c41a5d0"
import uuid
uuid.uuid4()   # random
uuid.uuid7()   # time-ordered โ€” Python 3.14+

Store UUIDs in a native uuid column (Postgres) or BINARY(16) (MySQL), not as 36-character strings โ€” that's 16 bytes instead of 36+ and much faster comparisons. To generate a batch in your browser, use our UUID generator, which produces both v4 and v7 from the browser's secure random source.

What about ULID, Snowflake and v1/v6?

  • ULID โ€” the community predecessor of v7: also 48 bits of milliseconds plus 80 random bits, written as 26 Crockford Base32 characters. Fine if you already use it; v7 is now the standardized equivalent and fits native UUID columns.
  • Snowflake IDs โ€” 64-bit time-ordered integers (Twitter, Discord). Smaller, but need a coordinated worker ID per generator.
  • UUIDv1 / v6 โ€” time-based with a node ID that was historically the machine's MAC address, which leaks hardware identity. v6 is a re-ordered v1 kept for legacy migration; RFC 9562 recommends v7 for new systems.
  • UUIDv8 โ€” a vendor-defined format for custom layouts.
FREQUENTLY ASKED

Frequently asked questions

Is UUIDv7 better than UUIDv4?

+

For database primary keys, usually yes: UUIDv7 is time-ordered, so inserts append to the end of B-tree indexes, giving faster writes and smaller indexes. UUIDv4 is better when an identifier must not reveal its creation time.

Can UUIDv7 collide?

+

Only in theory. Each UUIDv7 has 74 random bits that must be unique among IDs generated in the same millisecond. You would need around 160 billion IDs in a single millisecond for a 50% chance of one collision.

Does UUIDv7 leak information?

+

Yes. Its first 48 bits are the creation time in Unix milliseconds, which anyone can decode. Avoid exposing UUIDv7 values publicly when creation time is sensitive.

What is RFC 9562?

+

RFC 9562, published in May 2024, is the current UUID specification. It replaces RFC 4122 and adds UUID versions 6, 7 and 8, recommending version 7 for new time-ordered identifiers.

Are UUIDs secure enough for session tokens?

+

They should not be used that way. The UUID specification warns against assuming UUIDs are unguessable, and UUIDv7 contains a predictable timestamp. Use a dedicated cryptographically random token of at least 128 bits.

TOOLS IN THIS GUIDE

Tools used in this guide

KEEP READING