If your primary key is a UUIDv4, every insert lands at a random position in the B-tree.
PostgreSQL 18 gives you a one-function fix — and a new problem that most posts about it skip.
## The old problem, concretely
A UUIDv4 is 122 random bits, so consecutive inserts scatter across the whole index. Three things follow, and they compound:
- The pages you are writing to are rarely in shared_buffers, so you read before you write.
- More pages get dirtied per transaction, so more full-page writes hit the WAL.
- Leaf pages split in the middle rather than at the end, leaving them half full.
A BIGSERIAL has none of this. Inserts append to one hot page that never leaves cache. That single difference is the entire reason UUID keys acquired their reputation.
## What UUIDv7 changes
RFC 9562 standardised UUIDv7 in 2024 specifically to close that gap. The first 48 bits are a Unix millisecond timestamp; the rest is random. Values generated near each other in time therefore sort near each other in the index, and inserts concentrate on the right edge again.
PostgreSQL 18 ships it in core:
sql
CREATE TABLE orders (
id uuid DEFAULT uuidv7() PRIMARY KEY,
...
);
Postgres goes slightly beyond the spec here — it packs a 12-bit sub-millisecond fraction after the timestamp, which makes values strictly monotonic within a backend session rather than merely near-sorted. ## The part you have to decide deliberately UUIDv7 is not opaque. Anyone holding one of your IDs can read the millisecond it was created. It is a single function call:
sql
SELECT uuid_extract_timestamp(id) FROM orders LIMIT 1;
For an internal order ID, that is usually harmless. For a user ID that appears in a public URL, you have published your signup timeline — and given anyone who collects two IDs a way to estimate your growth rate between them. That is competitive intelligence you did not intend to hand out, obtained with no access to anything. It also does not fix the third UUID problem. It is still 128 bits, in the table and in every index and every foreign key that references it. ## Where that leaves it UUIDv7 over v4 for internal keys and write-heavy tables, almost always. For anything you hand to a client, ask one question first: is creation time something I am happy to disclose? Which UUID version is on your busiest table right now?