UUID v4 vs v7: Which Should You Use?
UUID v4 vs v7 compared for developers — random vs time-ordered, database index performance, sortability, and when to switch from v4 to v7 in your stack.
UUIDs are everywhere — primary keys, request trace IDs, session tokens, file names. For years, UUID v4 (random) was the default: generate it, use it, move on. Then UUID v7 landed in the RFC 9562 spec in 2024, and the question became: should I switch? The answer depends on whether you care about database index performance and sortability — which, in most production systems, you do.
This post covers what makes v4 and v7 different, which one belongs in which situation, and the practical tradeoffs that actually matter when you’re choosing a UUID version for a new project or considering a migration.
TL;DR: Use v4 for globally random IDs where order doesn’t matter. Use v7 when you’re using UUIDs as database primary keys — the time-ordered structure improves B-tree index performance significantly. Generate both versions at GoGood.dev UUID Generator.
What UUID v4 and v7 are
A UUID is a 128-bit identifier formatted as 32 hex digits in the pattern xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx. The M field identifies the version. Both v4 and v7 are in common use today.
UUID v4 — fully random:
f47ac10b-58cc-4372-a567-0e02b2c3d479
^^^^
version = 4
122 bits are randomly generated. The other 6 bits carry the version and variant markers. There’s no structure beyond that — just random bits. Two v4 UUIDs generated a millisecond apart look completely different.
UUID v7 — time-ordered random:
018f1a2b-3c4d-7ef0-a123-456789abcdef
^^^^^^^^^^^^^^^^
first 48 bits = Unix timestamp in milliseconds
^
version = 7
The first 48 bits encode a Unix millisecond timestamp. The remaining 74 bits are random. UUIDs generated close together sort close together — both lexicographically and by creation time.
UUID v7 was standardized in RFC 9562 (published April 2024), replacing the previous draft spec (RFC 4122). Most language libraries have added v7 support in the past year.
The core difference: sortability
v4 UUIDs are random. Insert 1,000 of them into a database table with a UUID primary key, and they scatter randomly across the index. Every new row goes into a different location in the B-tree, forcing page splits and fragmentation as the index grows.
v7 UUIDs are monotonically increasing within a millisecond. New rows land at the end of the index, the same way auto-increment integers do. No random scatter, no page splits.
This is the single biggest practical difference. At small scale, it’s invisible. At large scale — millions of rows, high insert rates — v4 primary keys cause measurable write amplification and index fragmentation that v7 avoids.
Benchmark context: PostgreSQL tests comparing UUID v4 vs v7 as primary keys consistently show v7 with 20–50% lower write latency at high insert rates, and significantly smaller index size after bulk inserts. The exact numbers vary by workload, but the direction is consistent.
When to use UUID v4
Random, non-sequential IDs where order is irrelevant. Good candidates:
- One-time tokens: password reset tokens, email verification links, API keys. These are used once, looked up by exact match, and never sorted. Random is fine.
- Session IDs: no need for ordering; randomness is a security property here.
- External-facing resource IDs in legacy systems: if you’re already using v4 and the system is stable, the migration cost rarely justifies switching.
- Any context where the ID is opaque and never used in a range query or sort: if your queries are always
WHERE id = $1, index fragmentation doesn’t matter.
v4 is also universally supported — every UUID library has had it since the beginning. If you’re not sure your stack supports v7 yet, v4 is the safe choice.
When to use UUID v7
Database primary keys — especially in high-insert-rate tables. Good candidates:
- Any table with a UUID primary key in PostgreSQL, MySQL, or SQLite where you’re doing bulk inserts or expect the table to grow large.
- Distributed systems where you need a globally unique, sortable ID: v7 gives you the sortability of a sequence with the global uniqueness of a UUID.
- Event log tables and audit trails: time-ordered IDs mean rows arrive in temporal order without a separate
created_atsort column (though you should keep the column for human readability). - API pagination with cursor-based paging: v7 UUIDs can act as cursors for keyset pagination without an additional sequence column.
-- v7 primary key enables efficient keyset pagination
SELECT * FROM events
WHERE id > '018f1a2b-3c4d-7000-0000-000000000000' -- cursor
ORDER BY id
LIMIT 50;
This works with v7 because IDs sort in creation order. With v4, the same query returns random rows.
Generating UUIDs in code
JavaScript / Node.js
The uuid package (v9+) supports both versions:
import { v4 as uuidv4, v7 as uuidv7 } from 'uuid';
const randomId = uuidv4();
// 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
const orderedId = uuidv7();
// '018f1a2b-3c4d-7ef0-a123-456789abcdef'
For PostgreSQL in Node.js, prefer v7 for primary keys:
import { v7 as uuidv7 } from 'uuid';
const newRecord = await db.query(
'INSERT INTO events (id, type, payload) VALUES ($1, $2, $3) RETURNING *',
[uuidv7(), 'user.created', payload]
);
Python
The standard library uuid module has v4. For v7, use uuid-utils or uuid7:
import uuid
# v4 — standard library
record_id = str(uuid.uuid4())
# 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
# v7 — requires uuid-utils or uuid7 package
from uuid_utils import uuid7
record_id = str(uuid7())
# '018f1a2b-3c4d-7ef0-a123-456789abcdef'
PostgreSQL
PostgreSQL 17 adds native uuidv7() support:
-- PostgreSQL 17+
SELECT uuidv7();
-- Earlier versions: use pgcrypto extension or application-level generation
-- Or use the uuid-ossp extension with gen_random_uuid() for v4
SELECT gen_random_uuid();
For older PostgreSQL, generate v7 in the application layer and pass it in. Don’t use gen_random_uuid() (v4) for high-write tables if you care about index performance.
Using a UUID generator online
When you need a UUID for a test fixture, seed data, config value, or documentation example, GoGood.dev UUID Generator generates them instantly:
Select the version (V4 for random, V7 for time-ordered), set the quantity, choose the output format (standard, no hyphens, braces, URN), and copy:
This is useful for populating test fixtures, seeding databases with known IDs, or generating config values without spinning up a REPL.
Common questions when switching from v4 to v7
“Can I mix v4 and v7 in the same table?”
Yes. Both are valid UUIDs in the same column. The performance benefit of v7 only applies to new rows — existing v4 rows in the index don’t get re-ordered. In practice, a gradual migration (new rows use v7, old rows stay as v4) is fine for most use cases.
“Does v7 leak timing information?”
Yes. The first 48 bits are a millisecond-precision Unix timestamp. For externally exposed IDs (URLs, public APIs), this means anyone with the ID can extract an approximate creation time. If that’s a problem for your use case, v4 is safer. For internal IDs (database PKs not exposed in APIs), it’s typically not a concern.
“My ORM uses uuid_generate_v4() at the database level. Do I need to change that?”
Only if insert performance at scale is a concern. If your table has fewer than a few million rows and inserts are moderate, the difference is measurable but small. For new tables on high-write services, it’s worth switching.
“Is v7 supported in my database?”
- PostgreSQL 17: native
uuidv7()function - MySQL 9.0+:
UUID_TO_BIN(UUID(), 1)(time-ordered variant) - SQLite: no built-in; generate in application and store as TEXT or BLOB
- MongoDB: uses its own ObjectId format (timestamp-prefixed, similar to v7 in structure); v7 support varies by driver version
FAQ
What is the difference between UUID v4 and v7?
v4 is 122 bits of random data — fully random, no structure. v7 uses the first 48 bits as a Unix millisecond timestamp and fills the remaining 74 bits with random data. The result is a UUID that sorts in creation order, which significantly improves database index performance when used as a primary key.
Should I use UUID v4 or v7 for primary keys?
v7 for new tables, especially high-write ones. The time-ordered structure eliminates random B-tree page splits, reducing write amplification and index fragmentation. For existing tables using v4, migration is only worth the complexity at high scale (tens of millions of rows, high insert rates).
Is UUID v7 secure enough for tokens and session IDs?
Not ideal. The first 48 bits are predictable (they encode time), so v7 has less entropy than v4 for security-sensitive use cases. Use v4 for tokens, API keys, and session IDs where unpredictability matters.
When was UUID v7 standardized?
RFC 9562, published in April 2024, formally standardized UUID versions 6, 7, and 8, replacing the older RFC 4122. v7 has been in draft form since around 2021, so some libraries added support before the final RFC.
Can I use UUID v7 with existing UUID columns in PostgreSQL?
Yes. A UUID column stores 128-bit values regardless of version. You can insert v7 UUIDs into a column that currently holds v4 UUIDs — they’re the same data type. New v7 rows will cluster at the end of the index; existing v4 rows stay in their current positions.
The decision between v4 and v7 usually comes down to one question: is this UUID a database primary key on a table that needs to scale? If yes, use v7. If the ID is a token, external reference, or one-time identifier where sequence doesn’t matter, v4 is simpler and more widely supported.
For related developer tool topics: Cron Expression Cheat Sheet for Developers covers another common scheduling decision, and Unix Timestamp to Human Date: A Developer’s Quick Reference covers timestamp conversions that come up alongside time-ordered IDs.