UUID v4 vs v7: Which One to Use as a Database Key
Random UUIDs fragment your index. v7 fixes it by putting a timestamp at the front. Here is the trade-off.
Both are 128-bit identifiers you can generate anywhere without coordinating with a server. The difference is ordering, and it shows up as database performance.
v4: fully random
f47ac10b-58cc-4372-a567-0e02b2c3d479
122 random bits. Collisions are not a practical concern — you'd need to generate billions before the probability becomes worth thinking about.
The problem is insertion order. In a B-tree index, a random key lands in a random page. Every insert touches a different part of the index, so the pages you need are rarely in cache and the tree fragments. On a large, write-heavy table this is measurable.
v7: timestamp first, then random
018f4e2a-7c31-7b3e-8f6d-2a91c4d7e8b0
└─ 48-bit ms timestamp ─┘└── random ──┘
The first 48 bits are a Unix millisecond timestamp. Two consequences:
Sortable. Sorting by ID sorts by creation time. You often get to drop a created_at index entirely.
Sequential inserts. New rows land at the end of the index, like an auto-increment integer. Pages stay hot, the tree doesn't fragment, and bulk inserts get faster.
The trade-off
v7 leaks creation time. Anyone holding the ID can read the millisecond it was generated. That's usually fine, occasionally not — if your IDs are public and the creation time is sensitive, or if the rate of ID creation reveals business volume you'd rather not publish, use v4.
Practical recommendation
- Primary keys on a table with meaningful write volume → v7
- Public-facing identifiers where creation time is sensitive → v4
- Idempotency keys, request IDs, correlation IDs → either; v7 makes logs easier to sort
- Anything that must not be guessable in sequence → v4
What about auto-increment integers?
Still the fastest option, and still the right call for a single-database application. UUIDs earn their cost when you need IDs generated on the client, offline, or across shards without a coordinator — and when you don't want row counts leaking through sequential IDs.
Storage
Store as uuid in Postgres or BINARY(16) in MySQL — 16 bytes. Storing as VARCHAR(36) costs more than twice the space in the table and in every index that references it. It's a very common and entirely avoidable mistake.
Generating them
SwitchPDF UUID Generator produces v4 and v7 in bulk, using your browser's cryptographic RNG. Useful for seeding test data or grabbing a handful of IDs for a fixture file without wiring up a script.
Bottom line
Default to v7 for anything that becomes a database key — you get free time-sorting and a healthier index. Reach for v4 when the timestamp is something you'd rather not publish.
Related articles
Base64 Explained: When to Use It and When Not To
Base64 is not encryption and it makes your data bigger. Here is what it is actually for.
Converting CSV to JSON Without Breaking Quoted Fields
Splitting on commas works until a value contains a comma. Here is what a real CSV parser handles that a split() does not.
Converting JSON to YAML for Kubernetes and Docker Compose
Your API speaks JSON, your infrastructure speaks YAML. Converting between them has three gotchas worth knowing about.