ULID-Generator

Generieren Sie ULIDs — sortierbare Identifikatoren mit Timestamps für verteilte Systeme

Generating ULIDs — Sortable Unique Identifiers

ULID (Universally Unique Lexicographically Sortable Identifier) combines a millisecond-precision timestamp with cryptographic randomness to create identifiers that are both globally unique and chronologically sortable. Unlike UUID v4 which is entirely random and unsortable, ULIDs preserve creation order when sorted alphabetically — making them ideal for database primary keys where insert ordering matters for index performance, and for distributed systems where time-ordered identifiers enable efficient range queries and pagination.

ULIDs use Crockford Base32 encoding to produce compact 26-character strings that are URL-safe, case-insensitive, and contain no special characters. The format dedicates the first 10 characters to a 48-bit millisecond timestamp and the remaining 16 characters to 80 bits of randomness, providing both temporal ordering and collision resistance. All generation happens client-side using the Web Crypto API for the random component.

ULID Structure and Encoding

A ULID is a 128-bit value encoded as 26 Crockford Base32 characters:

  • Timestamp (48 bits / 10 chars): Milliseconds since Unix epoch — sortable until the year 10889
  • Randomness (80 bits / 16 chars): Cryptographically random data for uniqueness within the same millisecond

Crockford Base32 uses characters 0-9 and A-Z excluding I, L, O, U to avoid ambiguity with digits 1, 0, and each other. This makes ULIDs safe for case-insensitive systems, readable in logs, and unambiguous when communicated verbally or in printed documentation.

The timestamp-first layout ensures that ULIDs generated later always sort after earlier ones — 01HXYZ... (generated today) always sorts after 01HWY... (generated yesterday) in any alphabetical or binary comparison.

Advantages Over UUID for Database Keys

ULIDs address specific pain points of UUID v4 as database primary keys:

  • Index locality: New rows always append to the end of B-tree indexes rather than inserting at random positions, dramatically reducing page splits and write amplification
  • Natural ordering: SELECT * FROM events ORDER BY id returns chronological order without a separate created_at column
  • Range queries: Fetch all records from a specific time window using ID range comparison: WHERE id > ulid_from_timestamp('2024-03-01')
  • Compactness: 26 characters vs 36 for UUID (no hyphens), saving storage in high-volume tables
  • URL safety: No hyphens or special characters — ULIDs work directly in URL paths without encoding

Monotonic ULID Generation

When multiple ULIDs are generated within the same millisecond, monotonic mode ensures they remain ordered:

  • Standard mode: Each ULID gets independent random bits — ordering within the same millisecond is undefined
  • Monotonic mode: If the timestamp matches the previous ULID, the random component is incremented by 1 rather than regenerated — guaranteeing strict ordering even at sub-millisecond generation rates

Monotonic generation is important for high-throughput systems that may generate thousands of IDs per millisecond, where random ordering within a millisecond could violate application assumptions about ID progression.

Code Examples

ULID Generation and Timestamp Extraction

// Generate a ULID
const ulid = generateULID();
console.log(ulid); // "01HY5P2X8G0K3MNPQ7RSTV4W9Z"

// Extract timestamp from ULID
function extractTimestamp(ulid) {
  const ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
  const timeChars = ulid.slice(0, 10);
  let timestamp = 0;
  for (const char of timeChars) {
    timestamp = timestamp * 32 + ENCODING.indexOf(char.toUpperCase());
  }
  return new Date(timestamp);
}

console.log(extractTimestamp(ulid));
// "2024-03-15T10:30:45.123Z"

// ULIDs sort chronologically
const ids = [
  "01HY5P3000AAAAAAAAAAAAAA", // later
  "01HY5P2X8G0K3MNPQ7RSTV4W", // earlier
];
ids.sort(); // Natural sort = chronological order

Standards & Specifications

  • ULID Specification — Defines the ULID format, Crockford Base32 encoding, and monotonic generation behavior

Häufig Gestellte Fragen

What is the difference between ULID and UUID?

ULIDs are lexicographically sortable because they encode a timestamp in the first 48 bits, making them ideal for database primary keys where insertion order matters. UUIDs (v4) are fully random with no inherent ordering. ULIDs are also more compact: 26 characters in Crockford Base32 vs 36 characters for UUIDs.

What is Crockford Base32 encoding?

Crockford Base32 is an encoding that uses 32 characters (0-9 and A-Z excluding I, L, O, U) to represent binary data. It avoids ambiguous characters that look similar in certain fonts, making ULIDs easier to read, copy, and communicate without errors.

Are ULIDs sortable by creation time?

Yes. The first 10 characters of a ULID encode a Unix timestamp in milliseconds, so ULIDs generated later will always sort after earlier ones lexicographically. The remaining 16 characters are random, ensuring uniqueness even for IDs created in the same millisecond.

When should I use ULID instead of UUID?

Use ULIDs when you need sortable identifiers (time-series data, database primary keys with B-tree indexes, event logs). Use UUIDs when you need maximum compatibility with existing systems, or when sortability could leak timing information in security-sensitive contexts.