UUID Versions Explained — Stop Using v4 for Everything
UUID v4 is the default everywhere, but it fragments your database indexes. Here's when to use v4, v7, and ULID — and why your DBA will thank you for switching.
Somewhere in your codebase, there’s a line that looks like this: id = uuid.v4(). It works. Your tests pass. Your users never complain. And your DBA quietly seethes every time she looks at the query plan.
UUID v4 became the default because it’s simple, universally available, and collision-resistant enough that you’ll never actually hit a conflict. But “it works” and “it’s the right tool” aren’t the same thing — and for database primary keys, v4 is actively making your system slower as it scales.
What UUID v4 Actually Does to Your Database
Relational databases use B-tree indexes for primary keys. A B-tree keeps data sorted so the database can find rows in O(log n) time. When you insert a new row, the database needs to place it in the correct sorted position in the index.
With UUID v4, every new ID is random. 550e8400-e29b-41d4-a716-446655440000 might be followed by f47ac10b-58cc-4372-a567-0e02b2c3d479 — there’s no relationship between consecutive inserts. The database has to navigate to a random leaf node in the B-tree every single time.
This causes two problems at scale:
- Page splits: When a B-tree leaf page fills up, the database has to split it into two pages and update the parent. With random inserts, splits happen frequently across the entire index — not just at the end.
- Cache misses: The database buffer pool keeps recently-used pages in memory. Sequential inserts keep hitting the same “hot” pages. Random inserts scatter reads across the entire index, blowing out the cache and forcing disk reads.
On a table with millions of rows and high write throughput, this index fragmentation adds up to real latency. If your EXPLAIN ANALYZE shows index scans taking longer than they should, random UUIDs may be part of the diagnosis.
The UUID Family Tree
UUID v1: Timestamps, MAC Addresses, and Why We Don’t Use It
UUID v1 was the original “sortable” UUID. It encodes a 60-bit timestamp in 100-nanosecond intervals since October 1582, combined with a clock sequence and your machine’s MAC address. The result is roughly sortable by time.
The MAC address part is what killed v1. It leaks your server’s network interface identifier into every ID you generate — every user record, every order, every event. Security researchers demonstrated that v1 UUIDs from the same machine are predictable once you have one sample. Organizations started avoiding it for anything user-facing, and most UUID libraries mark it deprecated.
UUID v4: Random, Secure, and Badly Suited for Primary Keys
UUID v4 is 122 bits of cryptographically random data (the remaining bits encode the version). The collision probability for a billion UUIDs is roughly 1 in 10^18. For practical purposes, it’s zero.
That randomness is exactly what you want for security tokens, session IDs, API keys, and correlation IDs — anything where you need an ID that can’t be guessed or enumerated. For those use cases, keep using v4. The problem is that “can’t be guessed” and “database friendly” are opposite properties.
Want to experiment with UUID generation? The UUID Generator on IO Tools lets you generate v1, v4, v7, and other variants side by side to see the difference in structure.
UUID v7: The New Default You Should Be Using
UUID v7 was standardized by the IETF in RFC 9562 in May 2023. It puts a Unix timestamp in milliseconds in the most significant 48 bits, followed by a 4-bit version field, a 12-bit sequence counter, and 62 bits of random data.
What this means in practice: UUIDs generated later are lexicographically larger. Consecutive inserts land in adjacent positions in the B-tree. No random scatter, no unnecessary page splits, no cache thrashing. It behaves like an auto-increment integer from the database’s perspective — while still being globally unique without coordination.
The sequence counter within the same millisecond ensures monotonic ordering even for high-frequency generators. If you generate 10,000 UUIDs in one millisecond, they’ll still sort correctly. The random suffix preserves enough entropy that collisions between distributed systems remain astronomically unlikely.
For any new system using PostgreSQL, MySQL, or another relational database, UUID v7 should be your default for primary keys.
ULID: The Alternative That Came First
ULID (Universally Unique Lexicographically Sortable Identifier) was solving the same problem before UUID v7 existed. It uses 48 bits for the Unix timestamp in milliseconds and 80 bits of random data, encoded in Crockford’s Base32.
The result is a 26-character string that looks like 01ARZ3NDEKTSV4RRFFQ69G5FAV instead of the 36-character hyphenated UUID format. It’s URL-safe without encoding, sorts correctly as a string, and is case-insensitive.
ULID doesn’t have an IETF RFC — it has a community spec at ulid.github.io. That’s enough for most teams, but if you’re in a regulated environment that requires formally standardized identifiers, UUID v7 is the safer choice. ULID has strong library support in JavaScript and Go ecosystems, and if your team is already using it, there’s no urgent reason to switch.
Side-by-Side Comparison
| Property | UUID v1 | UUID v4 | UUID v7 | ULID |
|---|---|---|---|---|
| Sortable | Partially | No | Yes | Yes |
| Collision resistant | Yes | Yes | Yes | Yes |
| Database friendly | Partially | No | Yes | Yes |
| Privacy safe | No (MAC addr) | Yes | Yes | Yes |
| Standard body | IETF RFC 4122 | IETF RFC 4122 | IETF RFC 9562 | Community spec |
| Typical length | 36 chars | 36 chars | 36 chars | 26 chars |
| Entropy source | MAC + clock | Random | Timestamp + random | Timestamp + random |
When to Use Each One
UUID v7 — use this for database primary keys in any new system. It’s an IETF standard, has growing library support (native in PostgreSQL 17, available via libraries in every major language), and gives you B-tree-friendly ordering with no coordination required.
UUID v4 — keep using this for anything security-sensitive where randomness matters: session tokens, password reset tokens, API keys, OAuth state parameters, correlation IDs in logs. The unpredictability is a feature here, not a bug.
ULID — use it if your team is already on it, particularly in JavaScript or Go projects. The shorter format is genuinely nicer in URLs and logs. If you’re starting fresh, UUID v7 is the safer long-term bet simply because it has IETF backing.
UUID v1 — don’t. There’s no scenario where v1 is the right choice for new code.
Migration Considerations
If you’re running an existing system with UUID v4 primary keys, you don’t need to migrate immediately — and you shouldn’t do it casually. Foreign key relationships, application code, and potentially cached values all reference those IDs. A migration requires careful planning and almost certainly a maintenance window.
For most teams, the pragmatic approach is: use UUID v7 (or ULID) for all new tables and services. Accept that your legacy tables have v4 and manage the fragmentation with periodic index rebuilds if the performance impact is measurable. Don’t let perfect be the enemy of good.
If you’re greenfield — new project, new database, new tables — there’s no reason to reach for v4 for primary keys. The tooling is there. Generate some UUID v7 samples and see what you’re working with.
The Takeaway
UUID v4 is not wrong — it’s just frequently misapplied. Its randomness is a security property, and you should preserve it where security matters. For database primary keys, that randomness turns into a performance liability at scale.
UUID v7 solves the problem cleanly: monotonically increasing, globally unique, standardized, and already supported in the databases and ORMs you’re using. If you’re writing new database schema today, make v7 your default. Future-you — and your DBA — will notice the difference.
Install Our Extensions
Add IO tools to your favorite browser for instant access and faster searching
恵 Scoreboard Has Arrived!
Scoreboard is a fun way to keep track of your games, all data is stored in your browser. More features are coming soon!
Must-Try Tools
View All New Arrivals
View AllUpdate: Our latest tool was added on Jun 1, 2026
