Understanding ULID: Features, Specification, and Python Usage Compared to UUID
This article explains ULID—a lexicographically sortable, 128‑bit identifier combining timestamp and randomness—detailing its advantages over UUID, specifications, binary layout, common use cases, and provides Python code examples for generating and manipulating ULIDs.
ULID (Universally Unique Lexicographically Sortable Identifier) is a 128‑bit identifier that combines a millisecond‑precision timestamp with 80 bits of randomness, offering lexicographic ordering and URL‑safe encoding.
UUID has five versions; version 4 (random) is most common but can still collide. Unlike UUID, ULID embeds time, providing 1.21×10²⁴ unique values per millisecond and eliminating collision risk while remaining 128‑bit compatible.
Key ULID characteristics include:
Compatibility with the 128‑bit size of UUID.
1.21×10²⁴ unique IDs per millisecond.
Lexicographic (dictionary) ordering.
Encoded as a 26‑character Crockford Base32 string (no ambiguous characters, case‑insensitive, URL‑safe).
Monotonic ordering within the same millisecond when using the default algorithm.
Specification
Timestamp : 48‑bit integer representing UNIX time in milliseconds (valid until year 10889).
Randomness : 80‑bit random value, preferably generated with cryptographic quality.
Sorting : Characters are ordered lexicographically; the leftmost character is the most significant.
Encoding : Crockford Base32 alphabet (0123456789ABCDEFGHJKMNPQRSTVWXYZ) without I, L, O, U.
Binary layout (network 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 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Typical application scenarios include replacing auto‑increment primary keys, generating globally unique keys without database coordination, sharding databases by embedded timestamp, and ordering records when millisecond precision is sufficient.
Python usage (ulid‑py)
pip install ulid-pyCreating a new ULID:
>>> import ulid
>>> ulid.new()Creating a ULID from an existing UUID:
>>> import ulid, uuid
>>> value = uuid.uuid4()
>>> value
UUID('0983d0a2-ff15-4d83-8f37-7dd945b5aa39')
>>> ulid.from_uuid(value)Creating a ULID from a specific timestamp:
>>> import datetime, ulid
>>> ulid.from_timestamp(datetime.datetime(1999, 1, 1))Creating a ULID from raw randomness:
>>> import os, ulid
>>> randomness = os.urandom(10)
>>> ulid.from_randomness(randomness)Once you have a ULID object, you can retrieve its components:
>>> u = ulid.new()
>>> u.timestamp()
>>> u.randomness()For more information, see the ULID project on GitHub: https://github.com/ahawker/ulid .
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
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.