ULID vs UUID — When Sortable IDs Matter
UUID v4 is random, which causes problems for database indexes — random inserts fragment B-tree pages and degrade write performance. ULID (Universally Unique Lexicographically Sortable Identifier) solves this by embedding a millisecond timestamp in the first 10 characters, making ULIDs naturally sort by creation time while remaining unique.
A ULID looks like 01ARZ3NDEKTSV4RRFFQ69G5FAV — 26 Crockford Base32 characters. The first 10 encode the timestamp, the last 16 are random. This gives you time-ordering (good for sequential database inserts), monotonicity within a millisecond (multiple ULIDs in the same millisecond are guaranteed ordered), and global uniqueness.
Use ULIDs as primary keys when you need: natural time-ordering without a separate created_at index, better write performance than random UUIDs in PostgreSQL and MySQL, and human-readable time-sortable IDs. They're also URL-safe and case-insensitive in Crockford Base32.
Tips
- ULIDs are monotonic within a millisecond — multiple ULIDs generated in the same millisecond are guaranteed to sort correctly.
- PostgreSQL: store ULIDs as
TEXTorCHAR(26). No native ULID type yet, but sorting works correctly as text. - UUID v7 is the IETF-standardized alternative to ULID with similar time-ordering properties — both are good choices.
- Libraries:
ulid(npm),python-ulid(PyPI),ulid-rs(Rust crates.io).