UUID v4 vs v7 vs ULID: Which Should You Use as a Primary Key?
Choosing a primary key format is a decision you live with for the lifetime of your database. UUID v4 is ubiquitous but unsortable. UUID v7 solves that. ULID is a newer alternative. Here's what each one means for your database performance.
UUID v4 โ fully random
550e8400-e29b-41d4-a716-446655440000
UUID v4 is 128 bits of randomness (except 6 bits for version/variant). It's the default everywhere โ every language has a uuid() function that generates v4. The problem: random IDs cause B-tree index fragmentation. New rows insert at random positions in the index, causing page splits and poor cache locality. At scale, this measurably increases write latency.
UUID v7 โ time-ordered (RFC 9562)
019260a3-c6c0-7000-8d4e-f2d4a1c3b5e7 # first 48 bits = Unix timestamp in milliseconds
UUID v7 is sortable by creation time. The first 48 bits are a millisecond-precision Unix timestamp, followed by a version field and random data. This means new rows insert at the end of the index โ the same access pattern as auto-increment integers. Index fragmentation is eliminated.
UUID v7 is ideal for:
- PostgreSQL with
uuidcolumn type - Any system that needs globally unique IDs without a central coordinator
- Microservices generating IDs independently (no coordination needed)
ULID โ Stripe-style, URL-safe
01ARZ3NDEKTSV4RRFFQ69G5FAV # 26 chars, Crockford Base32, first 10 chars = timestamp
ULID (Universally Unique Lexicographically Sortable Identifier) is also time-ordered. It uses Crockford Base32 encoding โ only uppercase letters and digits, no ambiguous characters (0/O, 1/I). This makes it URL-safe and human-readable. Stripe customer IDs (cus_xyz) follow a similar pattern.
Side-by-side comparison
| Property | UUID v4 | UUID v7 | ULID |
|---|---|---|---|
| Sortable | โ Random | โ ms precision | โ ms precision |
| Index-friendly | โ ๏ธ Fragmentation | โ Sequential | โ Sequential |
| URL-safe | โ ๏ธ Contains - | โ ๏ธ Contains - | โ Base32 |
| Length | 36 chars | 36 chars | 26 chars |
| Bit collision risk | Very low | Very low | Very low |
| Language support | Everywhere | Growing (2023+) | Good (libraries) |
| DB native type | uuid / binary(16) | uuid / binary(16) | varchar(26) |
Recommendation
- โNew system, PostgreSQL โ use UUID v7. It's an RFC standard (RFC 9562, 2024), sortable, and compatible with any uuid column.
- โNew system, prefer short readable IDs โ use ULID. Great for customer-facing IDs in URLs.
- โExisting system already using v4 โ keep v4. Migration cost rarely outweighs the benefit unless you're experiencing index fragmentation at scale.
- โNeed compatibility with auto-increment ordering โ UUID v7 or ULID. Both sort chronologically.
Generate UUID v4, v7, ULID, and more in bulk with the UUID / ULID Generator โ includes a bit-level breakdown of each format so you can see exactly what each section encodes.
Try the related tool
UUID / ULID Generator โ free, runs 100% in your browser.
Open UUID / ULID Generator โEnjoyed this? Get notified when Pro launches.
