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 ১২৮ বিট প্রশস্ত, দুটি উপাদানে বিভক্ত:
| টাইমস্ট্যাম্প | র্যান্ডম |
|---|---|
| 01ARZ3NDEK | TSVE4RRFFQ69G5FAV |
| ৪৮ বিট — মিলিসেকেন্ডে Unix টাইমস্ট্যাম্প। ১ms নির্ভুলতায় তৈরির সময় এনকোড করে। ১০৮৮৯ সাল পর্যন্ত তারিখ কভার করে। | ৮০ বিট — ক্রিপ্টোগ্রাফিক্যালি নিরাপদ র্যান্ডম ডেটা। প্রতিটি ULID-এর জন্য নতুনভাবে তৈরি হয় (মনোটোনিক মোড ব্যবহার না হলে)। |
২৬টি Crockford Base32 অক্ষর হিসেবে এনকোড করা হয়: প্রথম 10টি অক্ষর টাইমস্ট্যাম্প উপস্থাপন করে, শেষ 16টি অক্ষর র্যান্ডম উপাদান উপস্থাপন করে।
Crockford Base32 অ্যালফাবেট
ULID-গুলো Crockford-এর Base32 এনকোডিং ব্যবহার করে, যা ৩৬টি আলফানিউমেরিক অক্ষরের মধ্যে ৩২টি ব্যবহার করে। অ্যালফাবেট হল: 0123456789ABCDEFGHJKMNPQRSTVWXYZ
চারটি অক্ষর ইচ্ছাকৃতভাবে বাদ দেওয়া হয়েছে:
IএবংLবাদ দেওয়া হয়েছে —1সংখ্যার সাথে সহজেই বিভ্রান্ত হয়Oবাদ দেওয়া হয়েছে —0সংখ্যার সাথে সহজেই বিভ্রান্ত হয়Uবাদ দেওয়া হয়েছে — তৈরি হওয়া আইডিতে দুর্ঘটনাক্রমে অশ্লীল শব্দ এড়াতে- এনকোডিংটি
case-insensitive—01ARZ3NDEKTSV4RRFFQ69G5FAVএবং01arz3ndektsv4rrffq69g5favএকই ULID
Crockford Base32 হেক্সাডেসিমালের চেয়ে বেশি দক্ষ (৩২টি প্রতীক বনাম ১৬টি) এবং Base64-এর চেয়ে বেশি মানব-পাঠযোগ্য (কোনো + / = অক্ষর নেই, কেস-অসংবেদনশীল)।
ULID বনাম UUID
ULID এবং UUID উভয়ই ১২৮-বিট আইডেন্টিফায়ার উপস্থাপন করে, কিন্তু এনকোডিং ও ডিজাইন লক্ষ্যে উল্লেখযোগ্যভাবে আলাদা:
| বৈশিষ্ট্য | ULID | UUID |
|---|---|---|
| ফরম্যাট | Crockford Base32 | হাইফেনযুক্ত hex |
| দৈর্ঘ্য | ২৬টি অক্ষর | ৩৬টি অক্ষর |
| টাইমস্ট্যাম্প | ৪৮-বিট Unix ms | নেই (v4) বা ৪৮-বিট ms (v7) |
| সর্টযোগ্য | হ্যাঁ — লেক্সিকোগ্রাফিক | না (v4) / হ্যাঁ (v7) |
| URL-নিরাপদ | হ্যাঁ (শুধু আলফানিউমেরিক) | হ্যাঁ (হাইফেনসহ) |
| নির্ভরতা | লাইব্রেরি প্রয়োজন | নেটিভ (crypto.randomUUID) |
| DB সমর্থন | CHAR(26) বা BINARY(16) হিসেবে সংরক্ষণ করুন | নেটিভ UUID কলাম টাইপ |
| স্পেসিফিকেশন | কমিউনিটি স্পেসিফিকেশন (ulid.github.io) | RFC 4122 / RFC 9562 |
আপনি যদি ইতিমধ্যে একটি UUID ইকোসিস্টেমে থাকেন (PostgreSQL uuid কলাম, REST API, UUID সমর্থনসহ ORM), তাহলে UUID v7 সাধারণত ULID-এর চেয়ে ভালো উপযুক্ত। আপনি যদি আরও সহজবোধ্য এনকোডিং পছন্দ করেন এবং পুরো স্ট্যাক নিয়ন্ত্রণ করেন, তাহলে ULID একটি চমৎকার পছন্দ।
ULID বনাম nanoid
ULID এবং nanoid উভয়ই সংক্ষিপ্ত, URL-নিরাপদ আইডেন্টিফায়ার তৈরি করে, কিন্তু তাদের ভিন্ন ডিজাইন লক্ষ্য রয়েছে:
| বৈশিষ্ট্য | ULID | NanoID |
|---|---|---|
| টাইমস্ট্যাম্প | হ্যাঁ — ৪৮-বিট Unix ms | না |
| সর্টযোগ্য | হ্যাঁ | না |
| ডিফল্ট দৈর্ঘ্য | ২৬টি অক্ষর | ২১টি অক্ষর |
| এনট্রপি | ৮০ বিট (র্যান্ডম উপাদান) | ~১২৬ বিট |
| অ্যালফাবেট | Crockford Base32 (৩২টি অক্ষর) | URL-safe Base64 (৬৪টি অক্ষর) |
| কাস্টমযোগ্য দৈর্ঘ্য | না | হ্যাঁ (যেকোনো দৈর্ঘ্য) |
| ব্যবহারের ক্ষেত্র | সময়-ক্রমানুযায়ী আইডি, DB প্রাইমারি কী | র্যান্ডম টোকেন, সংক্ষিপ্ত URL, API কী |
সময়-ক্রম প্রয়োজন হলে ULID বেছে নিন। সংক্ষিপ্ত, র্যান্ডম স্ট্রিংয়ে সর্বোচ্চ এনট্রপি প্রয়োজন হলে nanoid বেছে নিন।
ডেটাবেসে ULID ব্যবহার
আপনার প্রয়োজনীয়তার উপর নির্ভর করে ULID-গুলো বিভিন্ন উপায়ে ডেটাবেসে সংরক্ষণ করা যায়:
কোড উদাহরণ
ULID তৈরির জন্য ulid লাইব্রেরি প্রয়োজন (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)
}