← Back to Blog

UUID Selection Guide: v1 vs v4 vs v7 and ULID Comparison

Why ID Selection Matters

In distributed systems, assigning a unique identifier to every record is a fundamental need. Choosing the wrong ID scheme can lead to database index fragmentation, sorting difficulties, or even privacy leaks. This article systematically compares UUID versions and ULID to help you make the right choice for your scenario.

UUID Basics

A UUID (Universally Unique Identifier) is a 128-bit globally unique identifier, formatted as an 8-4-4-4-12 hex string:

550e8400-e29b-41d4-a716-446655440000

The core value of UUIDs is generating unique IDs without central coordination, making them a cornerstone of distributed systems. The spec has evolved; the latest is RFC 9562 (May 2024, superseding RFC 4122).

Version Details

UUID v1: Timestamp + MAC Address

Method: current timestamp (60 bit, 100ns precision) + machine MAC address (48 bit) + clock sequence (14 bit)

Pros:

  • Sortable by generation time
  • No random number generator needed

Cons:

  • Privacy: MAC address exposes the machine's physical location
  • Security: generation time and hardware info can be inferred
  • Multithreading: clock sequence coordination needed on the same machine
  • MAC addresses may be duplicated in virtualized environments

Use case: legacy system compatibility only; not recommended for new projects.

UUID v4: Pure Random

Method: 122 bits of randomness (6 bits for version/variant)

Pros:

  • Simple implementation; no timestamp or machine ID needed
  • No privacy leakage
  • Negligible collision probability (2¹²² space)

Cons:

  • Not sortable: completely random, no temporal ordering
  • Database index disaster: random inserts cause frequent B+ tree page splits and fragmentation
  • 36 bytes when stored as a string

Collision intuition: after generating 2.71 × 10¹⁸ v4 UUIDs, collision probability reaches 50%. At 1 billion IDs/second, that takes ~85 years.

Use case: unique identification where sorting isn't needed—e.g., temporary tokens, message IDs.

UUID v6: Reordered v1

Method: same timestamp + MAC as v1, but timestamp high-order bits first, so lexicographic order matches chronological order.

Position: solves v1's sorting problem but retains the MAC address drawback. Low real-world adoption.

UUID v7: Timestamp + Random (Recommended)

Method: first 48 bits are a Unix millisecond timestamp; remaining 74 bits are random (including version/variant bits)

Pros:

  • Time-sortable: lexicographic order equals chronological order—perfect for DB indexes
  • High write performance: monotonically increasing; B+ tree appends without page splits
  • Distributed-friendly: no machine ID needed; nodes generate independently
  • No privacy leak: no MAC address
  • UUID-compatible: still a standard 128-bit UUID

Cons:

  • Millisecond precision; uniqueness within the same millisecond relies on randomness
  • Generation time can be roughly inferred (usually fine, but be aware)

Use case: database primary keys for new projects, distributed event IDs, sortable unique identifiers. The recommended choice in 2025.

UUID v8: Custom

RFC 9562's v8 allows a fully custom 128-bit layout. For special needs like embedding business data. Not recommended for general use.

ULID

ULID (Universally Unique Lexicographically Sortable Identifier) is an alternative to UUID, designed specifically for sortability.

Structure

01ARZ3NDEKTSV4RRFFQ69G5FAV
└── Timestamp (48 bit) ──┘└── Random (80 bit) ──┘
  • 128 bits total, same as UUID
  • Encoded as 26-character Crockford Base32 string
  • Millisecond Unix timestamp

ULID vs UUID v7

Feature UUID v7 ULID
Length 128 bit 128 bit
String form 36 chars (with hyphens) 26 chars
Encoding Hexadecimal Crockford Base32
Sortable By time, lexicographically By time, lexicographically
Case sensitivity Insensitive Insensitive
URL-friendly Must handle hyphens No special characters
Standardized RFC 9562 Community spec
Ecosystem Native UUID libraries Requires dedicated library
Time precision Millisecond Millisecond

Recommendation

  • Prefer UUID v7: if you want to reuse existing UUID infrastructure and DB native types
  • Prefer ULID: if you value shorter strings and URL-friendliness, and don't mind an extra dependency

Database Primary Key Deep Dive

Why Random IDs Hurt Index Performance

In MySQL InnoDB, the primary key is the clustered index—data is physically stored in key order. Random UUID v4 inserts mean:

  1. New rows land at arbitrary positions in the B+ tree
  2. Frequent page splits cause write amplification
  3. Index fragmentation means more disk pages read per query
  4. Buffer pool hit rate drops

UUID v7 / ULID are time-increasing: new rows always append to the end of the index, improving write performance several-fold.

Scheme Comparison

Scheme Sortable Index Perf Storage Uniqueness Scale
Auto-increment Best 4-8 bytes Central allocation Single machine / small
UUID v4 Poor 16 bytes Decentralized Small-medium
UUID v7 Excellent 16 bytes Decentralized Any scale
ULID Excellent 16 bytes Decentralized Any scale
Snowflake Excellent 8 bytes Needs machine ID Large-scale distributed

Practical Advice

  • New projects: prefer UUID v7 or ULID as primary key
  • Existing systems: keep the current scheme; migration cost usually isn't worth it
  • Ultra-large scale: consider Snowflake (8 bytes, more space-efficient)
  • Public APIs: avoid exposing auto-increment IDs (enumerable); use UUID/ULID

Security Notes

Random Number Quality

The random portion of UUID v4 and v7 must use a cryptographically secure RNG (CSPRNG):

  • Math.random(): not crypto-safe, predictable
  • ❌ Simple PRNGs
  • ✅ Node.js: crypto.randomBytes()
  • ✅ Python: secrets module (not random)
  • ✅ Java: SecureRandom
  • ✅ Go: crypto/rand

Don't Use UUIDs as Security Tokens

UUIDs are designed for uniqueness, not unpredictability. Session tokens, CSRF tokens, and API keys should use dedicated token schemes (e.g., 256-bit random + HMAC).

v1 Privacy Risk

UUID v1 contains the MAC address; attackers can infer:

  • The machine's physical network location
  • The approximate generation time
  • Whether IDs came from the same machine

Never use v1 in new systems.

Language Library Recommendations

Language UUID v4 UUID v7 ULID
JS/TS uuid uuid (v9.0+) ulid
Python uuid (stdlib) uuid-utils python-ulid
Java java.util.UUID com.github.f4b6a3:uuid-creator ulid-creator
Go google/uuid github.com/google/uuid (v1.6+) oklog/ulid
Rust uuid crate uuid crate (v1.10+) ulid crate

JavaScript Example

import { v4, v7 } from 'uuid';
import { ulid } from 'ulid';

const idV4 = v4();  // 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
const idV7 = v7();  // '018f6b22-9c2a-7c8b-9e3f-4a5b6c7d8e9f'
const ul  = ulid();  // '01ARZ3NDEKTSV4RRFFQ69G5FAV'

Python Example

import uuid
from ulid import ULID

id_v4 = uuid.uuid4()
id_v7 = uuid.uuid7()  # Python 3.14+
ul = str(ULID())

Decision Tree

Need a globally unique ID?
├── Need time-based sorting?
│   ├── Want standard UUID format? → UUID v7 ✅
│   └── Want shorter strings? → ULID ✅
├── No sorting needed?
│   └── UUID v4 (general purpose)
└── Need max performance and short ID?
    └── Snowflake (requires machine ID allocation)
← Back to Blog