Fundamentals 7 min read

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.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Why ULID Beats UUID: A Deep Dive into Lexicographically Sortable IDs

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()  # 01ARZ3NDEKTSV4RRFFQ69G5FAV

ULID 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             80bits

Components

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

ULID diagram
ULID diagram
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Distributed SystemsPythonuuidULIDidentifier
Liangxu Linux
Written by

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.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.