UUID v4 vs v7: Which Should You Use in 2026?
UUID v4 has been the default random identifier for over a decade. UUID v7 arrived in RFC 9562 (2024) and promises better database performance through time-ordering. Here is what changed, what it means for your schema, and how to choose the right version.
The Problem With Fully Random IDs
If you have ever noticed your PostgreSQL or MySQL database slowing down as a table grows past a few million rows, random UUIDs may be a contributing factor. Here is why.
A UUID v4 looks like this:
550e8400-e29b-41d4-a716-446655440000
f47ac10b-58cc-4372-a567-0e02b2c3d479
3e4a1b7c-9d2f-4e8a-b5c6-0f1a2b3c4d5e
Each one is generated entirely from a cryptographically secure random number generator. The bits are unpredictable by design. That randomness is great for security, but it is terrible for database indexes.
Most relational databases use a B-tree index for primary keys. A B-tree is an ordered data structure. When you insert a new row, the database has to find the correct sorted position in the index and write the new value there. With sequential IDs like auto-increment integers, every new row goes at the end of the index - a simple, fast append. With random UUIDs, every insert lands at a random position in the index. At scale, this causes index page splits: the database has to split full index pages to make room, causing fragmentation, increased I/O, and slower writes.
At Facebook's scale, switching from random UUIDs to time-ordered identifiers reduced index write amplification by over 50% on certain tables. The principle applies at any scale once your table has millions of rows.
UUID v7 was designed specifically to solve this problem while preserving the global uniqueness and decentralized generation that make UUIDs valuable in the first place.
What Is UUID v4?
UUID v4 is defined in RFC 4122 (2005) and is the most widely used UUID version today. Its structure is simple:
xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
where 4 = version bits
y = variant bits (8, 9, a, or b)
All remaining bits - 122 bits - are filled with cryptographically random data. This gives a collision probability so low it is effectively impossible in practice: generating 1 billion UUIDs per second for the next 100 years would still give you less than a 50% chance of a single collision.
UUID v4 properties:
- 122 bits of randomness - maximum entropy
- Not sortable - generated IDs have no meaningful order
- No embedded timestamp - you cannot extract creation time from the ID itself
- Decentralized - no coordination needed between generators
- Universally supported - every language and database supports it out of the box
What Is UUID v7?
UUID v7 is defined in RFC 9562 (published April 2024) and is the modern replacement for v4 in database contexts. It encodes a Unix timestamp in the most significant bits, making the UUIDs sort in creation order:
018f8e2a-4d3c-7b2a-9e4f-1a2b3c4d5e6f
018f8e2a-4d3d-7c1b-8e5a-2b3c4d5e6f7a
018f8e2a-4d3e-7d0c-7f6b-3c4d5e6f7a8b
^^^^^^^^^
48-bit Unix timestamp in milliseconds (most significant bits)
The structure of UUID v7:
- Bits 0–47: Unix timestamp in milliseconds
- Bits 48–51: Version field (0b0111 = 7)
- Bits 52–63: Sub-millisecond precision or sequence counter (12 bits)
- Bits 64–65: Variant field
- Bits 66–127: Random data (62 bits)
The result: UUIDs generated at the same millisecond are sub-sorted by a sequence counter and random suffix, while UUIDs generated at different times sort chronologically. Insert 10,000 rows per second and each new row goes near the end of the B-tree index, just like an auto-increment integer.
Side-by-Side Comparison
| Property | UUID v4 | UUID v7 |
|---|---|---|
| RFC | RFC 4122 (2005) | RFC 9562 (2024) |
| Random bits | 122 | 62 + 12 (seq/random) |
| Sortable by time | No | Yes |
| Embedded timestamp | No | Yes (ms precision) |
| B-tree index performance | Poor (random inserts) | Excellent (sequential) |
| Language support (2026) | Universal | Growing rapidly |
| Use as security token | Ideal | Acceptable (less entropy) |
| Timestamp extractable | No | Yes |
Real-World Performance Impact
Let us look at what random vs. time-ordered UUIDs mean in practice for a PostgreSQL table with a UUID primary key.
Scenario: Orders table with UUID v4 primary key
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
customer_id UUID NOT NULL,
total DECIMAL(10,2),
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- After 10M rows: index size ~800MB, INSERT takes ~8ms average
-- After 50M rows: INSERT takes ~25ms, bloat ratio ~3.5x
Same table with UUID v7 primary key
-- Using a UUID v7 generator (PostgreSQL extension or application-side)
CREATE TABLE orders (
id UUID PRIMARY KEY, -- set by application using UUID v7
customer_id UUID NOT NULL,
total DECIMAL(10,2),
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- After 10M rows: index size ~380MB, INSERT takes ~2ms average
-- After 50M rows: INSERT takes ~3ms, bloat ratio ~1.1x
The difference is dramatic at scale. UUID v7's sequential insertion pattern means the database rarely needs to split index pages, resulting in smaller, denser indexes and dramatically faster writes.
Step-by-Step: Migrating From v4 to v7
If you have an existing application using UUID v4 as primary keys, here is how to migrate safely:
- Add UUID v7 generation to your application layer. Use a library:
uuidv9+ in Node.js (uuidv7()),uuid7in Python,ramsey/uuidv4.7+ in PHP. - Do not migrate existing rows. UUID v7 is for new rows only. Keep your existing v4 IDs. Both versions are valid UUID format and coexist in the same column.
- Add a new column for new tables. For entirely new tables, use UUID v7 from day one. For existing tables, you cannot retroactively gain sort-ordering on historical rows.
- Update your default generator. Replace
gen_random_uuid()(PostgreSQL) orUUID()(MySQL) with application-generated UUID v7 values. - Rebuild indexes after bulk inserts. If you bulk-insert historical data, run
REINDEXorVACUUM FULLafterward to compact the index.
Generating UUID v7 in common languages
// Node.js (uuid v9+)
import { v7 as uuidv7 } from 'uuid';
const id = uuidv7();
// => '018f8e2a-4d3c-7b2a-9e4f-1a2b3c4d5e6f'
# Python (uuid7 library)
from uuid_extensions import uuid7
id = str(uuid7())
// Java (java-uuid-generator)
import com.fasterxml.uuid.Generators;
UUID id = Generators.timeBasedEpochGenerator().generate();
// PHP (ramsey/uuid)
use Ramsey\Uuid\Uuid;
$id = Uuid::uuid7()->toString();
Generate UUID v4 and v7 Instantly
Use our free UUID generator to create v4 and v7 UUIDs in bulk, validate existing UUIDs, and inspect embedded timestamps. 100% client side - nothing leaves your browser.
Open UUID GeneratorWhen to Use UUID v4
Despite the performance advantages of UUID v7, there are still good reasons to use v4:
- Security tokens and session IDs: When the identifier must be unguessable and contain no extractable metadata, v4's 122 bits of pure randomness is ideal. A UUID v7 leaks your server's clock to anyone who inspects the ID.
- API keys and invite codes: Same reasoning - you do not want any predictability in the ID space.
- Small tables: If a table will never exceed a few hundred thousand rows, the performance difference between v4 and v7 is negligible. Use v4 for simplicity.
- Legacy systems: If your stack only supports UUID v4 generation and the cost of adding a new library is not justified, v4 is fine.
- Cross-system correlation IDs: For distributed tracing where IDs are generated by many independent systems and never used as database primary keys, v4 is the universal standard.
When to Use UUID v7
- Database primary keys on high-write tables: Any table that receives more than a few thousand inserts per day benefits from time-ordered UUIDs. Orders, events, log entries, notifications - these are all candidates.
- Event sourcing and audit logs: UUID v7 lets you sort events by ID without a separate
created_atcolumn, because the timestamp is encoded in the ID itself. - Cursor-based pagination: Time-ordered IDs make cursor pagination trivial:
WHERE id > :last_seen_id ORDER BY id LIMIT 20. - Distributed systems with ordered event streams: When multiple services generate IDs and you need to merge and sort them chronologically, v7 gives you ordering for free.
- New greenfield projects: If you are starting a new application in 2026, default to UUID v7 for primary keys. The ecosystem support is now mature enough.
UUID v7 vs. ULID vs. CUID2
UUID v7 is not the only time-ordered identifier. Here is how it compares to popular alternatives:
- ULID: 26-character base32 encoded, 48-bit ms timestamp + 80 bits random. Human-readable, URL safe, but not standard UUID format - requires custom column types or storage as string.
- CUID2: Collision-resistant, time-seeded, starts with a letter. Not a UUID, not sortable by prefix in all cases. Good for URLs and human-readable IDs.
- UUID v7: Standard UUID format (stores in a UUID column natively), sortable, RFC-standardized in 2024. Best choice when you need UUID compatibility with sort benefits.
If your database has a native UUID type (PostgreSQL, MySQL 8+, CockroachDB), UUID v7 is the cleanest option because it requires no schema changes from UUID v4 - just change the generator.
Frequently Asked Questions
Does UUID v7 guarantee global uniqueness like v4?
Yes, with extremely high probability. UUID v7 has 74 bits of randomness (62 random + 12 sub-millisecond sequence bits). The collision probability is orders of magnitude lower than what is achievable in practice. For comparison, generating 1 trillion UUID v7s per millisecond would still give you a very low collision probability. The reduction in entropy vs. v4 is not meaningful in any real-world scenario.
Can I extract the creation timestamp from a UUID v7?
Yes. The first 48 bits are a Unix timestamp in milliseconds. In JavaScript: Number(BigInt('0x' + uuid.replace(/-/g,'').slice(0,12))) gives you the millisecond timestamp. This is both a feature (useful for debugging and pagination) and something to be aware of if you do not want to expose creation times in IDs visible to end users.
Is UUID v7 supported natively in PostgreSQL?
Not yet as a built-in function (as of PostgreSQL 16). You need to generate UUID v7 in your application layer or use an extension. The pg_uuidv7 extension adds a uuid_generate_v7() function. PostgreSQL stores UUID v7 in its native UUID type without any changes needed - it is still a valid 128 bit UUID.
Should I use UUID v7 or auto-increment integers for primary keys?
Auto-increment integers are still the fastest option for write-heavy single-database tables - they are 4 or 8 bytes vs 16 bytes for UUID, and they are perfectly sequential. Use UUID v7 when you need: distributed ID generation (multiple servers generating IDs without coordination), globally unique IDs across systems, or non-guessable IDs that still sort well. For a simple application with a single database, auto-increment is fine. For microservices or multi-region systems, UUID v7 is the right call.
What happened to UUID v5, v6, and other versions?
UUID v1 uses a MAC address and timestamp but leaks your network interface and has clock-based collision risks. UUID v3 and v5 are name-based (MD5 and SHA-1 hashing respectively) - useful for generating deterministic UUIDs from a namespace and name (e.g., a URL). UUID v6 is a field-compatible rearrangement of v1 for better sort performance but was superseded by v7 before wide adoption. UUID v8 is experimental custom format. For new applications in 2026: use v4 for tokens, v5 for deterministic IDs, and v7 for database primary keys.
Does UUID v7 work with ORMs like Hibernate, Django, or Sequelize?
Most modern ORMs treat UUIDs as opaque 128 bit values and accept any valid UUID version. You typically configure your ORM to use a custom ID generator function that returns a UUID v7 string instead of the default v4. In Spring Boot with Hibernate, use a custom @GenericGenerator. In Django, override the model's default field parameter. In Sequelize, set the model's defaultValue to your v7 generator function. The ORM does not care about the version - it just stores the bytes.
The Bottom Line
In 2026, the practical recommendation is clear: use UUID v7 for database primary keys on any table that will grow large, and use UUID v4 for security-sensitive tokens where maximum entropy and no extractable metadata is required. The RFC 9562 standard is finalized, ecosystem support has reached maturity, and the performance benefits are real and measurable.
If you are starting a new project today, there is no good reason to default to UUID v4 for database primary keys. The days of choosing between "UUID with poor index performance" and "integers with coordination overhead" are over.
Use our free tool here → UUID Generator to generate both v4 and v7 UUIDs, validate existing UUIDs, and inspect the embedded timestamp in v7 IDs.
Usman has 10+ years of experience securing enterprise infrastructure, managing high-traffic servers, and building zero-knowledge security tools. Read more about the author.