UUID v4 vs v7: Which Should You Use?
UUID v4 is random; UUID v7 is time-ordered. The choice has real consequences for database index locality, pagination, and debug-ability. Here is a practical guide to picking the right one.
UUID v4 became the default identifier format for most web systems some time in the mid-2010s. It was simple to generate, collision-free for any realistic scale, and it freed engineers from worrying about centralized ID services. Then people started putting billions of v4 UUIDs into relational databases, and a subtle performance problem became an expensive one. UUID v7, standardized in RFC 9562, is a response to that problem.
Why v4 is random
UUID v4 is 122 bits of randomness (with 6 bits fixed for version and variant). Two independently-generated v4 UUIDs have a negligible chance of collision — low enough that you can treat it as impossible for any reasonable deployment.
The randomness is also the problem. When v4 UUIDs are used as the primary key of a B-tree-indexed table, each insert lands in a random position in the index. Pages get fragmented, the working set of the index no longer fits in cache, and writes become slower than they should be.
What v7 changes
UUID v7 keeps the 128-bit size and the familiar xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format, but it partitions the bits differently:
- 48 bits of Unix-epoch milliseconds in the high part of the UUID.
- 4 bits of version (the number 7).
- 12 bits of sub-millisecond randomness.
- 2 bits of variant.
- 62 bits of additional randomness.
Because the high bits are time, UUIDs generated close together are close together in sort order. They are still collision-free for practical purposes — the trailing 74 bits of randomness see to that — but they no longer behave as pure random inserts.
Database impact
With v7 as a primary key, new rows go to the rightmost page of a B-tree. Pages fill sequentially. The working set of the index is small (the right edge of the tree plus a bit of the left side for lookups). Insert throughput goes up, index size goes down, and backup/restore completes faster.
There are measurements from Postgres and MySQL teams showing multi-fold improvements in bulk-insert throughput when switching from v4 to v7, or to a similar time-ordered format (ULID, which has the same basic shape). If you have ever profiled a slow ingestion pipeline and seen index maintenance dominate the CPU, this is why.
When v4 is still the right call
Time-ordered IDs leak information: anyone with two IDs can estimate how far apart in time they were created. That is fine for an internal row ID; it can be a privacy problem for public identifiers. If the ID will appear in a URL or an API response seen by third parties — a user-visible invite code, a password reset token, a public object handle — prefer v4 (or better, a purpose-built token format such as a high-entropy random string with a version prefix).
You also stay on v4 if your platform or library does not yet support v7. Adoption is solid in Go, Java, Python, Node, and Postgres, but some older ecosystems still only ship v4 generators.
ULID and KSUID: neighbors of v7
ULID and KSUID are earlier time-ordered identifier formats that predate v7. ULID uses Crockford base32 and is 26 characters long; KSUID is 27 characters of base62. Both are sort-friendly and mostly interchangeable with v7 at the system-design level.
If your stack already standardizes on ULID or KSUID, there is little reason to migrate to v7. If you are picking a format today and need interoperability with standard UUID tooling (databases, serializers, ORMs), v7 is the safer bet: it looks like a UUID, it stores as a UUID, and every v7-aware library round-trips cleanly.
Generating v4 in ToolPad
The UUID Generator on ToolPad currently produces v4 and v1 UUIDs in bulk — ideal for populating test fixtures, seeding a dev database, or generating sample payloads for load tests. For production primary keys, prefer a v7 generator in your application code or database so the timestamp reflects the actual insert time.
Practical rules
- Internal primary keys: v7 (or ULID / KSUID if you already use them). Time-ordering wins.
- Externally-visible tokens: v4 or a dedicated random-token format. Do not leak timing.
- Ephemeral request / trace IDs: either is fine; v7 helps when you sort logs by trace ID.
- Client-generated IDs: v7, and include the client's clock; the server can reconcile collisions on insert.