Dev Utilities·5 min read·By the StackUtils Team

UUID v4 vs v7 vs ULID: Which ID Format Should You Use?

Not all unique identifiers are created equal. Understand UUID v4, v7, ULID, and CUID — and how each choice affects your database performance.

Why Unique IDs Matter

In distributed systems, generating IDs without a central coordinator is a fundamental challenge. Auto-incrementing integers from a single database work well in simple setups, but they become bottlenecks when you need to:

  • Generate IDs across multiple services or database shards
  • Create records client-side before they reach the server
  • Merge data from different systems without ID collisions
  • Avoid exposing sequential record counts to users

UUID (Universally Unique Identifier) was the original solution, but newer formats like UUID v7 and ULID address significant limitations in the original design.

UUID Structure

A UUID is a 128-bit value typically represented as 32 hexadecimal digits in five hyphenated groups:

xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
         |         | |    |
         |         | |    └── Variant (N = 8, 9, a, or b)
         |         | └────── Version (M = 1, 4, 5, 7...)
         |         └──────── 12 hex chars (time mid)
         └────────────────── 8 hex chars (time low)

The version nibble (M) tells you how the UUID was generated. The variant nibble (N) identifies the UUID standard (most UUIDs are RFC 4122 variant 2).

UUID Versions Explained

UUID v1 – Timestamp + MAC Address

v1 embeds the current timestamp (100-nanosecond intervals since October 1582) and the machine's MAC address. While sortable by creation time, it exposes the host's MAC address (a privacy concern) and is predictable. Still used in some database drivers (Cassandra, for example).

UUID v4 – Random

v4 is the most widely used UUID version. It fills 122 bits with cryptographically secure random data (only 6 bits are reserved for version and variant). The probability of collision is approximately 1 in 5.3 × 10³⁶ — negligible for virtually every practical application.

Limitation: Random UUIDs are not time-sortable. In a B-tree database index, inserting random UUIDs fragments the index and causes frequent page splits, degrading write performance at scale.

UUID v5 – Namespace + SHA-1

v5 generates a deterministic UUID from a namespace UUID and a name string using SHA-1. The same inputs always produce the same UUID. Useful for generating stable IDs for well-known entities (e.g., a UUID for a URL or a DNS name).

UUID v7 – Unix Timestamp + Random (New Standard)

UUID v7 (standardized in RFC 9562, 2024) encodes a millisecond-precision Unix timestamp in the most significant bits, followed by random bits. This makes v7 UUIDs naturally time-sortable, which dramatically improves database index locality compared to v4.

A UUID v7 looks like:018f9c4a-9b00-7123-8456-426614174000

ULID – Universally Unique Lexicographically Sortable Identifier

ULID was designed specifically to address UUID's sortability problem. It encodes a 48-bit millisecond timestamp followed by 80 bits of randomness, encoded as 26 characters in Crockford's Base32 (uppercase, no ambiguous characters like I, L, O, U):

01HZ9VKPD4R5F6G7H8JK9MABCD
  |||||||||| ||||||||||||||||
  Timestamp  Randomness
  (48 bits)  (80 bits)

ULIDs are lexicographically sortable as strings, making them ideal for databases that use string-based sorting (like DynamoDB) or when you want sort order to reflect creation time.

Side-by-Side Comparison

FormatLengthSortable?Random bitsStandard
UUID v436 chars (with hyphens)❌ No122 bitsRFC 4122
UUID v736 chars (with hyphens)✅ Yes (ms)74 bitsRFC 9562 (2024)
ULID26 chars✅ Yes (ms)80 bitsULID spec (community)
UUID v136 chars (with hyphens)✅ Yes (100ns)MinimalRFC 4122
UUID v536 chars (with hyphens)❌ Deterministic0 (SHA-1)RFC 4122

Database Performance: Sequential vs. Random

The primary reason to prefer UUID v7 or ULID over UUID v4 in most new projects is B-tree index performance. When you insert a random UUID (v4) as a primary key in PostgreSQL, MySQL, or SQLite:

  • The new record lands at a random position in the index tree.
  • Pages that are 100% full must split, writing half their data to a new page.
  • The index becomes fragmented, increasing storage usage and slowing reads.

With UUID v7 or ULID, new records land near the end of the index (since timestamps are monotonically increasing), just like auto-incrementing integers. This means minimal page splits, better write throughput, and more compact indexes.

Benchmarks on PostgreSQL show UUID v7 delivering 2–4x higher insert throughput than UUID v4 at scale, with significantly lower index bloat.

Which Should You Use?

✅ Use UUID v7 for most new projects

It is the modern standard (RFC 9562), time-sortable for database performance, and has excellent library support in Node.js, Python, Go, and Rust.

✅ Use UUID v4 when simplicity matters most

crypto.randomUUID() is built into every modern browser and Node.js 19+. If you're not at a scale where index performance matters, v4 is fine.

✅ Use ULID for string-sortable IDs

DynamoDB, Redis, and log systems where string sort order should reflect time. Also useful when you want IDs that look cleaner than hyphenated UUIDs.

✅ Use UUID v5 for stable, deterministic IDs

When the same input should always produce the same ID — for content addressing, seeding databases, or generating IDs for well-known entities.

⚠️ Avoid UUID v1 in new code

It exposes the host MAC address and has weaker randomness. Use v7 instead for time-ordered IDs.

Generate UUIDs instantly

Use our free browser-based UUID Generator to create RFC 4122 compliant UUIDs in bulk.