เครื่องสร้าง 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 | สุ่ม |
|---|---|
| 01ARZ3NDEK | TSVE4RRFFQ69G5FAV |
| 48 บิต — Unix timestamp ในหน่วย millisecond เข้ารหัสเวลาที่สร้างด้วยความแม่นยำ 1ms ครอบคลุมวันที่จนถึงปี 10889 | 80 บิต — ข้อมูลสุ่มที่ cryptographically secure สร้างใหม่สำหรับ ULID แต่ละตัว (เว้นแต่ใช้ monotonicity mode) |
เข้ารหัสเป็น Crockford Base32 26 ตัวอักษร: 10 ตัวอักษรแรกแทน timestamp อีก 16 ตัวอักษรสุดท้ายแทนส่วนสุ่ม
Crockford Base32 Alphabet
ULID ใช้ Crockford's Base32 encoding ซึ่งใช้ 32 จาก 36 ตัวอักษรตัวเลขและตัวอักษร alphabet คือ: 0123456789ABCDEFGHJKMNPQRSTVWXYZ
สี่ตัวอักษรถูกยกเว้นโดยตั้งใจ:
IและLถูกยกเว้น — สับสนกับตัวเลข1ได้ง่ายOถูกยกเว้น — สับสนกับตัวเลข0ได้ง่ายUถูกยกเว้น — หลีกเลี่ยงคำหยาบโดยไม่ตั้งใจใน ID ที่สร้าง- การเข้ารหัสเป็น
case-insensitive—01ARZ3NDEKTSV4RRFFQ69G5FAVและ01arz3ndektsv4rrffq69g5favเป็น ULID เดิม
Crockford Base32 มีประสิทธิภาพมากกว่า hexadecimal (32 สัญลักษณ์ vs 16) และอ่านได้ง่ายกว่า Base64 (ไม่มีตัวอักษร + / =, case-insensitive)
ULID vs UUID
ULID และ UUID ทั้งคู่แสดง identifier 128 บิต แต่แตกต่างกันอย่างมีนัยสำคัญในการเข้ารหัสและเป้าหมายการออกแบบ:
| คุณสมบัติ | ULID | UUID |
|---|---|---|
| Format | Crockford Base32 | Hex พร้อมขีดกลาง |
| ความยาว | 26 ตัวอักษร | 36 ตัวอักษร |
| Timestamp | Unix ms 48 บิต | ไม่มี (v4) หรือ 48-bit ms (v7) |
| เรียงลำดับได้ | ใช่ — lexicographic | ไม่ (v4) / ใช่ (v7) |
| URL-safe | ใช่ (alphanumeric เท่านั้น) | ใช่ (พร้อมขีดกลาง) |
| Dependencies | ต้องการ library | Native (crypto.randomUUID) |
| DB support | เก็บเป็น CHAR(26) หรือ BINARY(16) | Native UUID column type |
| Spec | Community 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 แต่มีเป้าหมายการออกแบบต่างกัน:
| คุณสมบัติ | ULID | NanoID |
|---|---|---|
| Timestamp | ใช่ — Unix ms 48 บิต | ไม่ |
| เรียงลำดับได้ | ใช่ | ไม่ |
| ความยาวเริ่มต้น | 26 ตัวอักษร | 21 ตัวอักษร |
| Entropy | 80 บิต (ส่วนสุ่ม) | ~126 บิต |
| Alphabet | Crockford Base32 (32 ตัวอักษร) | URL-safe Base64 (64 ตัวอักษร) |
| ปรับความยาวได้ | ไม่ | ใช่ (ความยาวใดก็ได้) |
| Use case | Time-ordered ID, DB primary key | Random token, short URL, API key |
เลือก ULID เมื่อต้องการ time-ordering เลือก nanoid เมื่อต้องการ entropy สูงสุดใน string สุ่มที่สั้น
การใช้ ULID ในฐานข้อมูล
ULID สามารถเก็บในฐานข้อมูลได้หลายวิธีขึ้นอยู่กับความต้องการ:
ตัวอย่างโค้ด
การสร้าง ULID ต้องใช้ ulid library (มีสำหรับ JavaScript, Python, Go, Rust และอื่นๆ):
// 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)# 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-- 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;// 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)
}