Whether you're building APIs, databases, or distributed systems, UUIDs are everywhere. They usually look something like this::
f47ac10b-58cc-4372-a567-0e02b2c3d479
Everyone knows what they look like, but almost nobody can confidently answer the question: which UUID version should I use? UUID v1? v4? v7? What happened to v2, v3, v5, and the brand new v6, v7, v8?
The confusion is completely justified. RFC 4122 standardized the widely used UUID versions (v1, v3, v4, and v5), while v2 came from the older DCE specification. RFC 9562 (2024) later added v6, v7, and v8. Each version exists for a reason, and picking the wrong one has real consequences — especially when your UUID is going to be a primary key in a database.
This post breaks down every version, when to use it, when to avoid it, and — most importantly for database folks — which ones play nicely with indexes and which ones will silently wreck your query performance.
First: What Is a UUID, Exactly?
A UUID (Universally Unique Identifier) is a 128-bit identifier. It is usually written as 32 hexadecimal digits grouped into five sections:
xxxxxxxx-xxxx-Axxx-Bxxx-xxxxxxxxxxxx
Two of those digits are special:
-
A (the first digit of the third group) is the version — it tells you which algorithm produced the UUID.
1= v1,4= v4,7= v7, and so on. -
B (the first digit of the fourth group) is the variant — almost always
8,9,a, orb. You rarely need to think about this one.
So you can identify any UUID's version at a glance. Look at that third group:
| Version | Example |
|---|---|
| v1 | f47ac10b-58cc-1e2b-9e96-0800200c9a66 |
| v2 | 79ee8720-8ff9-21f1-80f6-d73a18d6a4c2 |
| v3 | f47ac10b-58cc-3e2b-9e96-0800200c9a66 |
| v4 | f47ac10b-58cc-4e2b-8b6d-0e02b2c3d479 |
| v5 | f47ac10b-58cc-5e2b-8b6d-0e02b2c3d479 |
| v6 | f47ac10b-58cc-6e2b-9e96-0800200c9a66 |
| v7 | 01890f3a-2a7a-7e2b-9e96-0e02b2c3d479 |
| v8 | f47ac10b-58cc-8e2b-9e96-0e02b2c3d479 |
(Those are illustrative examples so you can see the version digit clearly.)
Version by Version
You can use this tool to generate any uuid version from here https://safedevtools.com/uuid-generator/
UUID v1 — Time-Based + MAC Address
How it works: v1 combines a 60-bit timestamp (100-nanosecond intervals since 1582), a clock sequence, and the generating machine's MAC address.
f47ac10b-58cc-1e2b-9e96-0800200c9a66
timestamp MAC address
Pros:
- Roughly sortable in generation order — newer UUIDs tend to be bigger than older ones.
- Unique without any randomness guessing game (per machine).
Cons:
- Leaks information. Anyone who sees the UUID learns the machine's MAC address and an approximate creation time. That's a genuine security concern.
- Only roughly sequential; the timestamp doesn't strictly increase between UUIDs from different machines.
- Traditionally uses the machine's MAC address, although modern implementations often substitute a randomly generated node identifier to avoid leaking hardware information. Best for: Legacy systems that already use v1, or cases where you genuinely need time ordering and don't care about leaking machine identity. For almost everything else, prefer v7.
Database verdict: Better than v4 for indexes (values trend upward), but the information leak and imperfect ordering make it a poor default.
UUID v2 — DCE Security
How it works: This is v1 plus a POSIX UID/GID of the local user, intended for DCE (Distributed Computing Environment) security contexts.
Pros: Essentially none for general use.
Cons: It is rarely implemented — most libraries don't even ship it, and RFC 4122 itself barely covers it.
Best for: Almost nothing. If you reach for v2, stop and pick v7 instead.
Database verdict: Treat as unavailable. You'll struggle to even find a library that generates it.
UUID v3 — Name-Based, MD5
How it works: v3 derives the UUID deterministically from a namespace plus a name, hashed with MD5. Same namespace + same name = same UUID, every single time.
namespace: DNS → 6ba7b810-9dad-11d1-80b4-00c04fd430c8
name: "example.com"
result: v3 UUID (always the same value)
Pros:
- Deterministic — no randomness, no storage needed to look up an existing ID.
- Great for generating a stable identifier from something you already have (a URL, an email, a username).
Cons:
- MD5 is weak by modern standards.
- Not unique per row by itself — if two rows map to the same name, they get the same UUID.
Best for: Situations where you want the same entity to always resolve to the same ID across systems without a lookup table.
Database verdict: Fine as a stable foreign key if your "name" is genuinely unique, but it does not help with ordering or index performance.
UUID v4 — Random
How it works: v4 just generates 122 random bits (the other 6 are fixed version/variant bits). That's it.
f47ac10b-58cc-4e2b-8b6d-0e02b2c3d479
Pros:
- Secure — cryptographically strong randomness means nothing about your machine, time, or user is exposed.
- Trivially simple to generate; it's the default in nearly every language and library.
- Collision probability is effectively zero (122 random bits).
Cons:
- Completely random order. This is the big one for databases (see below).
- Not sortable, not predictable, not compressible.
Best for: Public-facing identifiers, security tokens, event IDs, and any case where you want an opaque identifier with zero information leakage. It is the classic "default" for a reason.
Database verdict: ⚠️ The worst choice for large table indexes. Random values inserted into a B-tree index cause page splits — every insert lands at a random position, forcing the database to reorder pages. With millions of rows this can cause dramatic index fragmentation, slower writes, and poor cache locality.
UUID v5 — Name-Based, SHA-1
How it works: Identical concept to v3, but uses the stronger SHA-1 hash instead of MD5.
namespace: URL → 6ba7b811-9dad-11d1-80b4-00c04fd430c8
name: "https://example.com/posts/42"
result: v5 UUID (always the same value)
Pros:
- Deterministic, like v3, but with a better hash.
- Stable across systems.
Cons:
- Same determinism caveat: identical inputs → identical UUID.
- SHA-1 is also showing its age, though it's fine for this non-security use.
Best for: Exactly the same use cases as v3 — stable IDs derived from names. Prefer v5 over v3 when your library supports both.
Database verdict: Same as v3 — acceptable for stable keys, useless for ordering.
UUID v6 — Time-Ordered (Reordered v1)
How it works: v6 takes the v1 timestamp bits and reorders them so the most significant bits come first. This makes the UUIDs actually sortable in time order, fixing v1's biggest weakness.
Pros:
- Truly sortable — lexicographic order matches time order.
- Keeps the information content of v1.
Cons:
- Like v1, it can reveal timestamp information and may expose the node identifier if a real MAC address is used.
- Much newer and less widely supported than v7.
Best for: Systems that want time-ordered IDs but can't move to v7 yet.
Database verdict: Good for indexes — values insert in near-sequential order, minimizing page splits. But v7 is generally the better modern pick.
UUID v7 — Time-Ordered, Unix Timestamp (Recommended)
How it works: v7 puts a 48-bit Unix timestamp (milliseconds) in the high bits and fills the rest with random data.
01890f3a-2a7a-7e2b-9e96-0e02b2c3d479
↑ millisecond timestamp ↑ ↑ random bits
Pros:
- Sortable by creation time → plays beautifully with database indexes.
- Random tail bits → high entropy, low collision risk.
- Does not expose machine identity, though it does reveal the approximate creation time.
- Standardized in 2024 (RFC 9562) and now the recommended choice for new systems.
Cons:
- Newer; some older libraries and databases don't support it yet (though support is spreading fast).
Best for: Primary keys, database rows, and anything that lives in an indexed column. This is the modern default for new applications.
Database verdict: ✅ Excellent. Sequential inserts mean B-tree indexes stay compact, writes are fast, and range queries by time become trivial.
UUID v8 — Custom / Experimental
How it works: v8 is the "bring your own" version. It reserves the version digit for custom, vendor-defined layouts — you decide what the bits mean.
Pros:
- Maximum flexibility for proprietary schemes.
Cons:
- Zero portability — only your system understands the format.
Best for: Internal, single-vendor systems with very specific needs. Not something most projects should touch.
Database verdict: Entirely depends on how you design the layout. If you design it to be sortable, it can index well; otherwise, it's a coin flip.
The Database / Indexing Cheat Sheet
If you're choosing a UUID for a primary key or an indexed column, this is the table that matters:
| Version | Sortable / Index-friendly | Information leak | Typical use |
|---|---|---|---|
| v1 | Roughly | Yes (time + MAC) | Legacy time-based systems |
| v2 | No | Yes | Rarely used (DCE) |
| v3 | No | No (deterministic) | Stable ID from a name (MD5) |
| v4 | No | No | Opaque IDs, tokens, default |
| v5 | No | No (deterministic) | Stable ID from a name (SHA-1) |
| v6 | Yes | Yes (time + MAC) | Sortable legacy replacement |
| v7 | Yes | Timestamp only | New databases / primary keys |
| v8 | Custom | Custom | Vendor-specific schemes |
The short rules of thumb
- New system with a database? → v7. Sequential inserts, great indexes, no privacy leak.
- Opaque, non-database identifier (API key, event ID, public token)? → v4. Random and private.
- Same entity must map to the same ID everywhere (e.g., a user's ID derived from their email across services)? → v5 (or v3 if you're stuck on old libraries).
- You already have v1 in production? → v6 is the drop-in sortable upgrade.
Why random UUIDs (v4) hurt database performance
A typical B-tree index stores keys in sorted order. When you insert a random value:
- The database has to find the middle of the tree, split pages, and shuffle data.
- Repeated random inserts cause index fragmentation and bloated pages.
- Write throughput degrades, and the index becomes much bigger than it needs to be.
- Cached index pages are used inefficiently because consecutive inserts touch random pages.
This is the single most common UUID mistake in production. It doesn't matter at 1,000 rows — it absolutely matters at 10 million.
If you need a UUID that also tells you when it was created, v7 gives you time ordering and database friendliness without the security downsides of v1/v6.
A Note on Alternatives
UUIDs aren't the only option. You'll also hear about:
- ULID — a v7-style "sortable, readable ID" with a timestamp prefix.
- Snowflake / Twitter Snowflake IDs — 64-bit time+worker IDs, great for high-scale distributed systems.
- NanoID — smaller URL-safe strings; friendly but not natively sorted or native to most databases.
- Sequential integers / IDENTITY columns — simplest, best index performance, but globally guessable and hard to merge across systems.
The great thing about UUID v7 is that it gives you most of the benefits of ULID/Snowflake (time ordering + compactness + index-friendliness) while staying within the standard, universally supported UUID format.
TL;DR
- v4 — random, private, the classic default; bad for database indexes at scale.
- v1 / v6 — time-based; sortable-ish, but leak MAC address + time.
- v3 / v5 — deterministic from a name; use for stable IDs, not for ordering.
- v7 — timestamp + random; the modern choice for primary keys and indexed columns.
- v8 — custom; only for vendor-specific schemes.
When in doubt, reach for v7 if it will live in a database, and v4 if it will live in the world.
You can generate uuid from V1 to V8 from here:
https://safedevtools.com/uuid-generator/
Happy identifying. 🆔
Top comments (0)