Why ULID Beats UUID: A Deep Dive into Lexicographically Sortable IDs
This article explains the limitations of UUID, introduces ULID as a time‑and‑randomness based identifier with 1.21×10⁻²⁴ unique values per millisecond, details its specification, binary layout, and shows how to generate and manipulate ULIDs in Python for distributed systems.
Why Not UUID
UUID defines five versions, each with limitations. Version 1 requires a stable MAC address and is vulnerable to spoofing. Version 2 replaces the timestamp with POSIX UID/GID, inheriting the same issue. Versions 3 and 5 use MD5 or SHA‑1 hashing and need a unique seed, which can cause data‑structure fragmentation. Version 4 is purely random and provides no additional information. Even random UUID 4 carries a non‑zero collision probability.
ULID Features
ULID combines a millisecond‑precision timestamp with 80 bits of randomness, yielding 1.21×10⁻²⁴ unique IDs per millisecond, effectively eliminating collisions. It is 26 characters long, lexicographically sortable, URL‑safe, case‑insensitive, and fits within the 128‑bit space of UUIDs.
ulid() # 01ARZ3NDEKTSV4RRFFQ69G5FAVULID Specification (Python ulid‑py)
A ULID consists of a 48‑bit timestamp and an 80‑bit random component, encoded with Crockford's Base32 (characters I, L, O, U are omitted).
01AN4Z07BY 79KA1307SR9X4MV3
|----------| |----------------|
Timestamp Randomness
10chars 16chars
48bits 80bitsComponents
Timestamp
48‑bit integer representing UNIX time in milliseconds.
Provides uniqueness until the year 10889.
Randomness
80‑bit random number, preferably generated with cryptographic quality (e.g., os.urandom()).
Sorting
Characters are ordered lexicographically (ASCII order). Within the same millisecond, ordering is not guaranteed.
Encoding
Uses Crockford's Base32 alphabet: 0123456789ABCDEFGHJKMNPQRSTVWXYZ.
Binary Layout and Byte Order
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 32_bit_uint_time_high |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 16_bit_uint_time_low | 16_bit_uint_random |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 32_bit_uint_random |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 32_bit_uint_random |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+Use Cases
Replace auto‑increment primary keys, removing the need for DB‑side ID generation.
In distributed environments, serve as a globally unique, millisecond‑ordered alternative to UUID.
Leverage the embedded timestamp for time‑based sharding or partitioning.
When millisecond precision is sufficient, sort records directly by ULID instead of a separate timestamp column.
Python Usage
Install the library: pip install ulid-py Create a new ULID:
>> import ulid
>>> ulid.new()
<ULID('01BJQE4QTHMFP0S5J153XCFSP9')>Convert an existing UUID (or other 128‑bit value) to ULID:
>> import ulid, uuid
>>> value = uuid.uuid4()
>>> value
UUID('0983d0a2-ff15-4d83-8f37-7dd945b5aa39')
>>> ulid.from_uuid(value)
<ULID('09GF8A5ZRN9P1RYDVXV52VBAHS')>Create a ULID from a specific timestamp:
>> import datetime, ulid
>>> ulid.from_timestamp(datetime.datetime(1999, 1, 1))
<ULID('00TM9HX0008S220A3PWSFVNFEH')>Create a ULID from custom randomness:
>> import os, ulid
>>> randomness = os.urandom(10)
>>> ulid.from_randomness(randomness)
<ULID('01BJQHX2XEDK0VN0GMYWT9JN8S')>Retrieve components from a ULID object:
>> import ulid
>>> u = ulid.new()
>>> u
<ULID('01BJQM7SC7D5VVTG3J68ABFQ3N')>
>>> u.timestamp()
<Timestamp('01BJQM7SC7')>
>>> u.randomness()
<Randomness('D5VVTG3J68ABFQ3N')>GitHub repository: https://github.com/ahawker/ulid
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
