UUID v4 vs UUID v7
When to pick random over time-ordered — and vice versa. Updated for RFC 9562 (May 2024) and PostgreSQL 18's native uuidv7().
Side-by-side comparison
| Property | UUID v4 | UUID v7 |
|---|---|---|
| Source bits | 122 random + 6 fixed | 48 timestamp + 74 random + 6 fixed |
| Lexically sortable by creation time | No | Yes |
| Reveals creation time | No | Yes (millisecond precision) |
| Indexed insert performance | Random page writes (poor at scale) | Append-friendly (excellent) |
| Unguessability | 122 bits | 74 bits |
| Defined in | RFC 4122 (2005), RFC 9562 (2024) | RFC 9562 (2024) |
| Native PostgreSQL | gen_random_uuid() (since v13) | uuidv7() (since v18) |
| Best fit | Public IDs, tokens, file names | Database primary keys, event IDs, logs |
Why v7 inserts faster than v4 in indexed tables
Database B-trees store entries sorted by key. With a random v4 UUID as primary key, every insert lands on a random leaf page — the database must read that page from disk if it isn't cached, dirty it, and eventually flush it. As the table outgrows the buffer pool, almost every insert becomes a disk read followed by a disk write.
With v7, the first 48 bits encode the millisecond timestamp, so consecutive inserts go to the same hot page (or the next one over) — exactly the behavior an auto-increment integer would produce. The buffer pool serves nearly every write from memory and the WAL/redo stream stays sequential.
When v4 is still the right answer
- Public-facing identifiers where ordering should not be observable. Two adjacent v7 IDs reveal that one user signed up immediately before another.
- Security tokens. v4's 122 random bits offer more unguessability than v7's 74 random bits.
- Share links and capability URLs where leaking the creation time would help an attacker enumerate or correlate actions.
Mixed strategy: v7 internal, v4 external
A common pattern in 2026 is to use v7 as the primary key (for index performance) and a separate v4 as the externally exposed identifier (for privacy). The v4 column gets a unique secondary index. Lookups by external ID are O(log n) on the v4 index; everything else benefits from v7's locality on the primary key.
CREATE TABLE users (
id uuid PRIMARY KEY DEFAULT uuidv7(), -- internal, ordered
public_id uuid UNIQUE NOT NULL DEFAULT gen_random_uuid(), -- external, opaque
email text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX idx_users_public_id ON users (public_id);