ToolDeck

ULID Generator

تولید شناسه‌های یکتا با قابلیت مرتب‌سازی لغوی

تعداد

برای ایجاد ULID روی «تولید» کلیک کنید

ULID چیست؟

ULID (Universally Unique Lexicographically Sortable Identifier) یک قالب شناسه ۱۲۸ بیتی است که برای یکتا بودن و قابلیت مرتب‌سازی طبیعی طراحی شده است. بر خلاف UUID v4 که کاملاً تصادفی است، یک ULID یک زمان‌نمای Unix با دقت میلی‌ثانیه را در بیت‌های ابتدایی خود جاسازی می‌کند — و این تضمین می‌کند که شناسه‌های جدیدتر بعد از شناسه‌های قدیمی‌تر مرتب شوند.

ULIDها با الفبای Crockford Base32 رمزگذاری می‌شوند و یک رشته فشرده ۲۶ کاراکتری تولید می‌کنند که برای URL امن، حروف کوچک و بزرگ در آن یکسان، و عاری از کاراکترهای مبهم بصری است.

مشخصات ULID توسط Alizain Feerasta ایجاد شده و در سیستم‌های توزیع‌شده، معماری‌های رویدادمحور، و کلیدهای اصلی پایگاه داده که هم یکتایی و هم ترتیب زمانی اهمیت دارد، به‌طور گسترده استفاده می‌شود. ULIDها یک استاندارد IETF نیستند — بلکه یک مشخصه جامعه‌محور هستند که در ulid.github.io در دسترس است.

ساختار ULID

یک ULID ۱۲۸ بیت پهنا دارد و به دو بخش تقسیم می‌شود:

زمان‌نماتصادفی
01ARZ3NDEKTSVE4RRFFQ69G5FAV
۴۸ بیت — زمان‌نمای Unix بر حسب میلی‌ثانیه. زمان تولید را با دقت ۱ میلی‌ثانیه رمزگذاری می‌کند. تاریخ‌ها را تا سال ۱۰۸۸۹ پوشش می‌دهد.۸۰ بیت — داده تصادفی رمزنگاری‌شده امن. برای هر ULID از نو تولید می‌شود (مگر اینکه از حالت یکنواختی استفاده شود).

به صورت ۲۶ کاراکتر Crockford Base32 رمزگذاری می‌شود: ۱۰ کاراکتر اول نشان‌دهنده زمان‌نما و ۱۶ کاراکتر آخر نشان‌دهنده بخش تصادفی است.

الفبای Crockford Base32

ULIDها از رمزگذاری Crockford Base32 استفاده می‌کنند که ۳۲ کاراکتر از ۳۶ کاراکتر الفبای عددی را به‌کار می‌برد. الفبا عبارت است از: 0123456789ABCDEFGHJKMNPQRSTVWXYZ

چرا این ۳۲ کاراکتر؟
0123456789ABCDEFGHJKMNPQRSTVWXYZ

چهار کاراکتر به‌عمد حذف شده‌اند:

  • I و L حذف شده‌اند — به‌راحتی با رقم 1 اشتباه گرفته می‌شوند
  • O حذف شده است — به‌راحتی با رقم 0 اشتباه گرفته می‌شود
  • U حذف شده است — از ایجاد کلمات ناخواسته در شناسه‌های تولیدشده جلوگیری می‌کند
  • این رمزگذاری بین حروف کوچک و بزرگ تفاوتی قائل نمی‌شود (case-insensitive) — 01ARZ3NDEKTSV4RRFFQ69G5FAV و 01arz3ndektsv4rrffq69g5fav یک ULID یکسان هستند

Crockford Base32 نسبت به هگزادسیمال کارآمدتر (۳۲ نماد در برابر ۱۶) و نسبت به Base64 خواناتر برای انسان است (بدون کاراکترهای + / = و با حروف کوچک/بزرگ یکسان).

ULID در برابر UUID

ULIDها و UUIDها هر دو نشان‌دهنده شناسه‌های ۱۲۸ بیتی هستند، اما از نظر رمزگذاری و اهداف طراحی تفاوت قابل توجهی دارند:

ویژگیULIDUUID
قالبCrockford Base32هگز با خط تیره
طول۲۶ کاراکتر۳۶ کاراکتر
زمان‌نما۴۸ بیتی Unix میلی‌ثانیهندارد (v4) یا ۴۸ بیتی میلی‌ثانیه (v7)
قابل مرتب‌سازیبله — لغویخیر (v4) / بله (v7)
امن برای URLبله (فقط الفبای عددی)بله (با خط تیره)
وابستگی‌هانیاز به کتابخانه داردبومی (crypto.randomUUID)
پشتیبانی پایگاه دادهذخیره به صورت CHAR(26) یا BINARY(16)نوع ستون UUID بومی
مشخصهمشخصه جامعه‌محور (ulid.github.io)RFC 4122 / RFC 9562

اگر از پیش در اکوسیستم UUID هستید (ستون‌های uuid در PostgreSQL، APIهای REST، ORMهای دارای پشتیبانی UUID)، UUID v7 معمولاً گزینه بهتری نسبت به ULID است. اگر رمزگذاری دوستانه‌تر برای انسان را ترجیح می‌دهید و کل پشته را کنترل می‌کنید، ULID انتخاب عالی است.

ULID در برابر nanoid

هر دو ULID و nanoid شناسه‌های کوتاه و امن برای URL تولید می‌کنند، اما اهداف طراحی متفاوتی دارند:

ویژگیULIDNanoID
زمان‌نمابله — ۴۸ بیتی Unix میلی‌ثانیهخیر
قابل مرتب‌سازیبلهخیر
طول پیش‌فرض۲۶ کاراکتر۲۱ کاراکتر
آنتروپی۸۰ بیت (بخش تصادفی)~۱۲۶ بیت
الفباCrockford Base32 (32 کاراکتر)Base64 امن برای URL (64 کاراکتر)
طول قابل تنظیمخیربله (هر طول)
موارد استفادهشناسه‌های زمان‌محور، کلیدهای اصلی پایگاه دادهتوکن‌های تصادفی، URLهای کوتاه، کلیدهای API

ULID را انتخاب کنید وقتی به ترتیب زمانی نیاز دارید. nanoid را انتخاب کنید وقتی به حداکثر آنتروپی در یک رشته کوتاه و تصادفی نیاز دارید.

استفاده از ULIDها در پایگاه‌های داده

ULIDها می‌توانند بسته به نیاز شما به چند روش در پایگاه‌های داده ذخیره شوند:

ذخیره به صورت CHAR(26)
ساده‌ترین رویکرد. ترتیب مرتب‌سازی لغوی حفظ می‌شود. اندکی بزرگ‌تر از ذخیره‌سازی باینری است اما برای انسان خوانا و اشکال‌زدایی آن آسان است.
ذخیره به صورت BINARY(16)
ULID را به نمایش باینری ۱۶ بایتی برای ذخیره‌سازی فشرده تبدیل کنید. در کد برنامه به رمزگذاری/رمزگشایی نیاز دارد. ترتیب مرتب‌سازی حفظ می‌شود.
PostgreSQL
به صورت CHAR(26) ذخیره کنید یا از نوع bytea برای ذخیره‌سازی باینری استفاده کنید. نوع ULID بومی وجود ندارد، اما ترتیب لغوی بر روی CHAR(26) به‌درستی کار می‌کند.
MySQL / MariaDB
از CHAR(26) CHARACTER SET ascii برای ذخیره‌سازی کارآمد استفاده کنید. ذخیره‌سازی باینری در BINARY(16) نیز کار می‌کند و فضا صرفه‌جویی می‌کند.
SQLite
به صورت TEXT ذخیره کنید. مقایسه متن پیش‌فرض SQLite به دلیل طراحی لغوی آن، ULIDها را به‌درستی مرتب می‌کند.
MongoDB
به صورت یک فیلد رشته‌ای ذخیره کنید. قابلیت مرتب‌سازی لغوی ULID به‌طور طبیعی با مقایسه رشته‌ای MongoDB سازگار است.

نمونه‌های کد

تولید ULID نیاز به کتابخانه ulid دارد (برای 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 ≈ 1.2 × 10^24 ممکن را در هر میلی‌ثانیه به ازای هر تولیدکننده فراهم می‌کند. احتمال تصادم در یک میلی‌ثانیه یکسان در تولیدکننده‌های مختلف برای هر سیستم عملی ناچیز است. برای تضمین‌های یکتایی دقیق، از حالت یکنواختی استفاده کنید که بخش تصادفی را برای شناسه‌های تولیدشده در همان میلی‌ثانیه افزایش می‌دهد.
آیا ULID همان UUID است؟
خیر — ULIDها و UUIDها رمزگذاری‌های متفاوتی از یک مقدار ۱۲۸ بیتی هستند. یک ULID نمی‌تواند مستقیماً جایگزین UUID در سیستم‌هایی شود که قالب UUID (هگز با خط تیره) را اعتبارسنجی می‌کنند. با این حال، در صورت نیاز می‌توانید بین نمایش‌های باینری ULID و UUID تبدیل انجام دهید.
حالت یکنواختی ULID چگونه کار می‌کند؟
در حالت استاندارد، بخش تصادفی ۸۰ بیتی برای هر ULID از نو تولید می‌شود. در حالت یکنواختی، وقتی چندین ULID در یک میلی‌ثانیه یکسان تولید می‌شوند، بخش تصادفی شناسه‌های دوم و بعدی برابر با مقدار تصادفی قبلی به‌علاوه یک است. این ترتیب یکنواخت دقیق را در یک میلی‌ثانیه بر روی یک تولیدکننده تضمین می‌کند.
آیا می‌توانم یک ULID را برای به‌دست‌آوردن زمان‌نمای تولید رمزگشایی کنم؟
بله. ۱۰ کاراکتر اول Crockford Base32 زمان‌نمای Unix ۴۸ بیتی میلی‌ثانیه را رمزگذاری می‌کنند. آن کاراکترها را با استفاده از الفبای Crockford Base32 رمزگشایی کنید، نتیجه را به عنوان زمان‌نمای Unix بر حسب میلی‌ثانیه تفسیر کنید و به یک تاریخ تبدیل کنید. این ابزار دقیقاً همین کار را انجام می‌دهد.
آیا ULID یک استاندارد رسمی است؟
خیر. ULID یک مشخصه جامعه‌محور است که در ulid.github.io نگهداری می‌شود. این استاندارد IETF مانند UUID نیست. این بدان معناست که هیچ RFC رسمی برای ارجاع وجود ندارد و پیاده‌سازی‌ها ممکن است اندکی متفاوت باشند. UUID v7 (RFC 9562، ۲۰۲۴) گزینه استانداردشده IETF با ویژگی‌های مشابه ترتیب زمانی است.