ToolDeck

เครื่องสร้าง ULID

สร้าง ID ที่ไม่ซ้ำกันและเรียงลำดับได้ตามตัวอักษร

จำนวน

คลิกสร้างเพื่อสร้าง ULID

ULID คืออะไร?

ULID (Universally Unique Lexicographically Sortable Identifier) คือรูปแบบ identifier 128 บิตที่ออกแบบให้ทั้ง unique และเรียงลำดับตามธรรมชาติ ต่างจาก UUID v4 ที่สุ่มทั้งหมด ULID ฝัง Unix timestamp ความแม่นยำ millisecond ใน high bits เพื่อให้แน่ใจว่า ID ที่สร้างภายหลังเรียงหลัง ID ที่สร้างก่อนหน้า

ULID เข้ารหัสด้วย Crockford's Base32 alphabet สร้าง string 26 ตัวอักษรที่กระชับซึ่ง URL-safe, case-insensitive และไม่มีตัวอักษรที่สับสนทางภาพ

ULID specification สร้างโดย Alizain Feerasta และใช้กันอย่างแพร่หลายใน distributed system, event sourcing และ database primary key ที่ทั้ง uniqueness และ time-ordering สำคัญ ULID ไม่ใช่ IETF standard — เป็น community specification ที่ ulid.github.io

โครงสร้าง ULID

ULID กว้าง 128 บิต แบ่งเป็นสองส่วน:

Timestampสุ่ม
01ARZ3NDEKTSVE4RRFFQ69G5FAV
48 บิต — Unix timestamp ในหน่วย millisecond เข้ารหัสเวลาที่สร้างด้วยความแม่นยำ 1ms ครอบคลุมวันที่จนถึงปี 1088980 บิต — ข้อมูลสุ่มที่ cryptographically secure สร้างใหม่สำหรับ ULID แต่ละตัว (เว้นแต่ใช้ monotonicity mode)

เข้ารหัสเป็น Crockford Base32 26 ตัวอักษร: 10 ตัวอักษรแรกแทน timestamp อีก 16 ตัวอักษรสุดท้ายแทนส่วนสุ่ม

Crockford Base32 Alphabet

ULID ใช้ Crockford's Base32 encoding ซึ่งใช้ 32 จาก 36 ตัวอักษรตัวเลขและตัวอักษร alphabet คือ: 0123456789ABCDEFGHJKMNPQRSTVWXYZ

ทำไมถึงเป็น 32 ตัวอักษรเหล่านี้?
0123456789ABCDEFGHJKMNPQRSTVWXYZ

สี่ตัวอักษรถูกยกเว้นโดยตั้งใจ:

  • I และ L ถูกยกเว้น — สับสนกับตัวเลข 1 ได้ง่าย
  • O ถูกยกเว้น — สับสนกับตัวเลข 0 ได้ง่าย
  • U ถูกยกเว้น — หลีกเลี่ยงคำหยาบโดยไม่ตั้งใจใน ID ที่สร้าง
  • การเข้ารหัสเป็น case-insensitive01ARZ3NDEKTSV4RRFFQ69G5FAV และ 01arz3ndektsv4rrffq69g5fav เป็น ULID เดิม

Crockford Base32 มีประสิทธิภาพมากกว่า hexadecimal (32 สัญลักษณ์ vs 16) และอ่านได้ง่ายกว่า Base64 (ไม่มีตัวอักษร + / =, case-insensitive)

ULID vs UUID

ULID และ UUID ทั้งคู่แสดง identifier 128 บิต แต่แตกต่างกันอย่างมีนัยสำคัญในการเข้ารหัสและเป้าหมายการออกแบบ:

คุณสมบัติULIDUUID
FormatCrockford Base32Hex พร้อมขีดกลาง
ความยาว26 ตัวอักษร36 ตัวอักษร
TimestampUnix ms 48 บิตไม่มี (v4) หรือ 48-bit ms (v7)
เรียงลำดับได้ใช่ — lexicographicไม่ (v4) / ใช่ (v7)
URL-safeใช่ (alphanumeric เท่านั้น)ใช่ (พร้อมขีดกลาง)
Dependenciesต้องการ libraryNative (crypto.randomUUID)
DB supportเก็บเป็น CHAR(26) หรือ BINARY(16)Native UUID column type
SpecCommunity spec (ulid.github.io)RFC 4122 / RFC 9562

หากอยู่ในระบบนิเวศ UUID แล้ว (PostgreSQL uuid column, REST API, ORM ที่รองรับ UUID) UUID v7 มักเหมาะกว่า ULID หากต้องการ encoding ที่เป็นมิตรกับมนุษย์มากกว่าและควบคุม stack ทั้งหมด ULID เป็นตัวเลือกที่ยอดเยี่ยม

ULID vs nanoid

ทั้ง ULID และ nanoid สร้าง identifier สั้น URL-safe แต่มีเป้าหมายการออกแบบต่างกัน:

คุณสมบัติULIDNanoID
Timestampใช่ — Unix ms 48 บิตไม่
เรียงลำดับได้ใช่ไม่
ความยาวเริ่มต้น26 ตัวอักษร21 ตัวอักษร
Entropy80 บิต (ส่วนสุ่ม)~126 บิต
AlphabetCrockford Base32 (32 ตัวอักษร)URL-safe Base64 (64 ตัวอักษร)
ปรับความยาวได้ไม่ใช่ (ความยาวใดก็ได้)
Use caseTime-ordered ID, DB primary keyRandom token, short URL, API key

เลือก ULID เมื่อต้องการ time-ordering เลือก nanoid เมื่อต้องการ entropy สูงสุดใน string สุ่มที่สั้น

การใช้ ULID ในฐานข้อมูล

ULID สามารถเก็บในฐานข้อมูลได้หลายวิธีขึ้นอยู่กับความต้องการ:

เก็บเป็น CHAR(26)
วิธีที่ง่ายที่สุด ลำดับการเรียง lexicographic ได้รับการรักษา ใหญ่กว่าการเก็บแบบ binary เล็กน้อยแต่อ่านได้และ debug ง่าย
เก็บเป็น BINARY(16)
ถอดรหัส ULID เป็น binary representation 16 bytes เพื่อการจัดเก็บที่กระชับ ต้องการการ encode/decode ใน application code ลำดับการเรียงได้รับการรักษา
PostgreSQL
เก็บเป็น CHAR(26) หรือใช้ประเภท bytea สำหรับการเก็บแบบ binary ไม่มีประเภท ULID แบบ native แต่การเรียง lexicographic บน CHAR(26) ทำงานถูกต้อง
MySQL / MariaDB
ใช้ CHAR(26) CHARACTER SET ascii เพื่อการจัดเก็บที่มีประสิทธิภาพ การเก็บแบบ binary ใน BINARY(16) ก็ทำงานได้และประหยัดพื้นที่
SQLite
เก็บเป็น TEXT SQLite's default text comparison เรียง ULID ได้ถูกต้องเนื่องจากการออกแบบ lexicographic ของมัน
MongoDB
เก็บเป็น string field ULID's lexicographic sortability ทำงานตามธรรมชาติกับ string comparison ของ MongoDB

ตัวอย่างโค้ด

การสร้าง ULID ต้องใช้ ulid library (มีสำหรับ JavaScript, Python, Go, Rust และอื่นๆ):

JavaScript (ulid npm)
// Using the 'ulid' npm package
import { ulid } from 'ulid'

const id = ulid()          // "01ARZ3NDEKTSV4RRFFQ69G5FAV"
const id2 = ulid()         // "01ARZ3NDEKXXXXXXXXXXXX..." (same ms, incremented random)

// With a custom timestamp
const id3 = ulid(1469918176385) // deterministic time portion

// Extract the timestamp back out
import { decodeTime } from 'ulid'
decodeTime(id)  // → 1469918176385 (Unix ms)
Python (python-ulid)
# Using python-ulid
from ulid import ULID

uid = ULID()
str(uid)                    # "01ARZ3NDEKTSV4RRFFQ69G5FAV"
uid.timestamp               # 1469918176.385
uid.datetime                # datetime(2016, 7, 30, 23, 36, 16, 385000, tzinfo=timezone.utc)

# Parse an existing ULID string
parsed = ULID.from_str("01ARZ3NDEKTSV4RRFFQ69G5FAV")
parsed.timestamp            # 1469918176.385
PostgreSQL
-- Store ULIDs as TEXT or CHAR(26)
CREATE TABLE events (
  id   CHAR(26) PRIMARY KEY DEFAULT gen_ulid(),  -- if using a PG extension
  name TEXT NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Or pass the ULID from your application layer
INSERT INTO events (id, name, created_at)
VALUES ('01ARZ3NDEKTSV4RRFFQ69G5FAV', 'user.signup', NOW());

-- Range queries are efficient because ULIDs sort chronologically
SELECT * FROM events
WHERE id > '01ARZ3NDEKTSV4RRFFQ69G5FAV'
ORDER BY id
LIMIT 20;
JavaScript (pure — no dependencies)
// Pure browser / Deno / Node.js implementation (no dependencies)
const CROCKFORD = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'

function encodeTime(ms) {
  let result = '', n = BigInt(ms)
  for (let i = 9; i >= 0; i--) {
    result = CROCKFORD[Number(n & 31n)] + result
    n >>= 5n
  }
  return result
}

function encodeRandom(bytes) {
  let n = 0n
  for (const b of bytes) n = (n << 8n) | BigInt(b)
  let result = ''
  for (let i = 15; i >= 0; i--) {
    result = CROCKFORD[Number(n & 31n)] + result
    n >>= 5n
  }
  return result
}

function generateULID() {
  const randomBytes = new Uint8Array(10)
  crypto.getRandomValues(randomBytes)
  return encodeTime(Date.now()) + encodeRandom(randomBytes)
}

คำถามที่พบบ่อย

ULID ไม่ซ้ำกันทั่วโลกไหม?
ใช่ — ด้วยความน่าจะเป็นสูงมาก ส่วนสุ่ม 80 บิตให้ค่าที่เป็นไปได้ 2^80 ≈ 1.2 × 10^24 ต่อ millisecond ต่อ generator ความน่าจะเป็นของ collision ภายใน millisecond เดิมระหว่าง generator ต่างกันไม่มีนัยสำคัญสำหรับระบบจริงใดๆ สำหรับการรับประกัน uniqueness ที่เข้มงวด ใช้ monotonic mode ซึ่งเพิ่ม random component สำหรับ ID ที่สร้างภายใน millisecond เดิม
ULID เหมือน UUID ไหม?
ไม่ — ULID และ UUID เป็นการเข้ารหัสต่างกันของค่า 128 บิต ULID ไม่สามารถแทนที่ UUID โดยตรงในระบบที่ validate UUID format (hex พร้อมขีดกลาง) อย่างไรก็ตาม คุณสามารถแปลงระหว่าง binary ULID และ UUID representation ได้หากจำเป็น
ULID monotonic mode ทำงานอย่างไร?
ในโหมดมาตรฐาน ส่วนสุ่ม 80 บิตสร้างใหม่สำหรับ ULID แต่ละตัว ในโหมด monotonic เมื่อสร้าง ULID หลายตัวภายใน millisecond เดิม ส่วนสุ่มของ ID ที่สองและถัดไปคือค่าสุ่มก่อนหน้าบวกหนึ่ง ทำให้มั่นใจว่าเรียงลำดับ monotonic ที่เข้มงวดภายใน millisecond บน single generator
สามารถถอดรหัส ULID เพื่อรับ generation timestamp ได้ไหม?
ใช่ 10 ตัวอักษร Crockford Base32 แรกเข้ารหัส Unix millisecond timestamp 48 บิต ถอดรหัสตัวอักษรเหล่านั้นโดยใช้ Crockford Base32 alphabet ตีความผลลัพธ์เป็น Unix timestamp ในหน่วย millisecond และแปลงเป็นวันที่ เครื่องมือนี้ทำสิ่งนั้นโดยตรง
ULID เป็นมาตรฐานอย่างเป็นทางการไหม?
ไม่ ULID เป็น community specification ที่ดูแลที่ ulid.github.io ไม่ใช่ IETF standard เหมือน UUID ซึ่งหมายความว่าไม่มี RFC อย่างเป็นทางการให้อ้างอิง และ implementation อาจแตกต่างกันเล็กน้อย UUID v7 (RFC 9562, 2024) คือ IETF-standardised alternative ที่มีคุณสมบัติ time-ordering ที่คล้ายกัน

เครื่องมือที่เกี่ยวข้อง