What Is a Unique Identifier?
Unique identifiers (UIDs) are strings or numbers that uniquely identify records in a database, distributed system, or communication protocol. The two most common formats in modern software are UUID (Universally Unique Identifier) and ULID (Universally Unique Lexicographically Sortable Identifier).
Choosing the wrong format can lead to poor database performance, difficult debugging, or compatibility issues. This guide explains both formats and helps you decide which to use.
UUID: The Universal Standard
UUID is defined in RFC 4122 and consists of 128 bits displayed as 32 hexadecimal characters split by hyphens:
550e8400-e29b-41d4-a716-446655440000
UUID version 4 (the most common) uses random bits for all fields. It has approximately 122 bits of randomness — about 5.3 × 10³⁶ possible values. The probability of collision when generating 1 billion UUIDs is about 1 in 10²⁰.
| UUID Version | Source | Use Case |
|---|---|---|
| v1 | MAC address + timestamp | Legacy, exposes device MAC |
| v3 | MD5 hash of namespace + name | Deterministic from name |
| v4 | Random | General purpose ✅ |
| v5 | SHA-1 hash of namespace + name | Deterministic from name |
| v7 | Unix timestamp + random | Sortable, modern ✅ |
ULID: Sortable by Design
ULID encodes a 48-bit millisecond timestamp followed by 80 bits of randomness, displayed as 26 Crockford Base32 characters:
01ARZ3NDEKTSV4RRFFQ69G5FAV
│──────────│──────────────│
10 chars 16 chars
(timestamp) (random)
Because the timestamp is the prefix, ULIDs sort lexicographically in chronological order. Records created at the same millisecond share a timestamp prefix but differ in the random suffix, preserving uniqueness.
Head-to-Head Comparison
| Feature | UUID v4 | UUID v7 | ULID |
|---|---|---|---|
| Length (string) | 36 chars | 36 chars | 26 chars |
| Sortable | ❌ | ✅ | ✅ |
| URL-safe | Needs encoding | Needs encoding | ✅ |
| Contains timestamp | ❌ | ✅ | ✅ |
| Standardized | RFC 4122 | RFC 9562 (2024) | Community spec |
| Database index performance | Fragmented | Good | Good |
| Readability | Low | Low | Moderate |
Database Performance: Why Sortability Matters
In B-tree indexes (PostgreSQL, MySQL), sequential inserts are far more efficient than random inserts. UUID v4's randomness causes index fragmentation: each new row is inserted at a random position in the index tree, causing frequent page splits and cache misses.
ULIDs and UUID v7 solve this by using monotonically increasing values (most of the time), which allows new records to be appended near the end of the index, dramatically reducing write overhead in high-throughput systems.
Benchmark data from various PostgreSQL experiments shows 3–5× better insert performance with sortable IDs on large tables (100M+ rows).
When to Use UUID v4
- Maximum compatibility — Every database, language, and framework understands UUID.
- No timestamp leakage — If you don't want creation time embedded in IDs.
- Simple integration — Available in SQL (
gen_random_uuid()in PostgreSQL 13+), Node.js (crypto.randomUUID()), Python (uuid.uuid4()). - Low-to-medium throughput — Fine for most applications with under millions of daily records.
When to Use ULID
- High-write databases — Event sourcing, logs, audit trails.
- URL-safe IDs — No hyphens or special characters.
- Debugging — You can extract creation time from the ULID itself.
- Sorted lists — Use as a cursor for pagination without storing
created_atseparately.
When to Use UUID v7
UUID v7 (formalized in RFC 9562, April 2024) offers the best of both worlds: standard UUID format with embedded Unix timestamp for sortability. It's increasingly supported in databases (PostgreSQL 17+) and libraries. For new projects, UUID v7 is often the best choice.
Practical Code Examples
// UUID v4 — Node.js built-in
const { randomUUID } = require('crypto');
const id = randomUUID(); // "550e8400-e29b-41d4-a716-446655440000"
// ULID — npm package
import { ulid } from 'ulid';
const id = ulid(); // "01ARZ3NDEKTSV4RRFFQ69G5FAV"
-- PostgreSQL UUID v4
SELECT gen_random_uuid();
-- PostgreSQL UUID v7 (pg 17+ or pgcrypto extension)
SELECT uuid_generate_v7();
→ Try UUID Generator or ULID Generator