PostgreSQL has had a native uuid column type since version 8.3 (2008), long before UUIDs were a default choice elsewhere, and functions like gen_random_uuid() made it trivial to generate them directly in SQL. That early first-class support is a big part of why UUID primary keys became such a common pattern in the Postgres ecosystem well before frameworks in other languages standardized on it.
UUID versions
- v1 — based on the current time and the network card's MAC address. Guarantees uniqueness but partially reveals when and on which device the identifier was created.
- v4 — fully random (aside from a few bits reserved to mark the version). The most common choice today precisely because it leaks no side information.
- v5 — deterministic, computed as a hash of a namespace and a string — the same input always produces the same UUID.
Why the collision probability is practically zero
In UUID v4, 122 bits are random. Even generating billions of UUIDs per second for centuries, the probability of even a single collision remains astronomically small — a fact mathematically grounded in the birthday paradox for a space this large.
Why you'd need this
- Generating database primary keys without a central counter or coordination between servers.
- Creating session, request, or transaction identifiers in distributed systems.
- Avoiding predictable sequential IDs that are easy to enumerate (unlike
1, 2, 3...).
UUID v7: a compromise between randomness and sorting
A fully random UUID v4 hurts database index performance — new rows get inserted at chaotic positions in the index tree instead of at the end. The newer UUID v7 solves this by embedding a timestamp in the leading bits of the identifier: values stay nearly as unique as v4, but sort naturally by creation time, like a sequential counter — which is exactly the property Postgres's own uuidv7() function (added in recent releases) was introduced to take advantage of.