تولیدکننده ULID - ایجاد شناسههای مرتبشدنی یکتا به صورت آنلاین و رایگان
ابزار رایگان تولید ULID، شناسههای یکتا و مرتبشدنی را به صورت آنی ایجاد میکند. تولید ULIDهای امن از نظر رمزنگاری برای پایگاههای داده، APIها و سیستمهای توزیعشده به صورت آنلاین.
تولیدکننده ULID
مستندات
ULID چیست؟
ULID (شناسهٔ قابل مرتبسازی واژهنامهای و یکتای همگانی) کدی ۲۶کاراکتری است که برای برچسبگذاری رکوردها در پایگاه داده یا یک رویداد در سیستم توزیعشده به کار میرود. این کد زمان فعلی را با دادهٔ تصادفی ترکیب میکند؛ بنابراین فهرستی از ULIDها که به ترتیب الفبایی مرتب شده باشد، بر اساس زمان ایجاد هرکدام نیز مرتب خواهد بود. تولیدکنندهٔ ULID ابزاری است که این کدها را تولید میکند.
ساختار ULID
یک ULID دو بخش دارد که بدون جداکننده، پشت سر هم نوشته میشوند:
- مهر زمانی (۱۰ کاراکتر): تعداد میلیثانیهها از ۱ ژانویهٔ ۱۹۷۰ (مبدأ یونیکس) که حداکثر ۴۸ بیت را پوشش میدهد.
- تصادفیبودن (۱۶ کاراکتر): ۸۰ بیت دادهٔ تصادفی که اضافه میشود تا دو ULID ایجادشده در یک میلیثانیه، تقریباً بهطور قطعی متفاوت باشند.
هر دو بخش با استفاده از الفبای Base32 کراکفورد نوشته میشوند: ارقام ۰–۹ و حروف A تا Z، بهجز حروف I، L، O و U. این چهار حرف حذف شدهاند، زیرا ممکن است با ارقام ۰ و ۱ یا با یکدیگر اشتباه شوند. حاصل، رشتهای کوتاه و آسان برای خواندن با صدای بلند است که میتوان آن را مستقیماً و بدون خطر در URL قرار داد.
نحوهٔ محاسبهٔ ULID
تولیدکنندهٔ ULID یک شناسه را در سه مرحله میسازد.
- زمان فعلی را بخوانید؛ برحسب میلیثانیه از مبدأ یونیکس. این یک عدد ۴۸بیتی است.
- ۸۰ بیت دادهٔ تصادفی تولید کنید؛ معمولاً با یک مولد عدد تصادفی امن از نظر رمزنگاری که ۱۰ بایت تصادفی تولید میکند.
- هر دو بخش را جداگانه کدگذاری کنید؛ با استفاده از الفبای Base32 کراکفورد: مهر زمانی ۴۸بیتی به ۱۰ کاراکتر تبدیل میشود و مقدار تصادفی ۸۰بیتی به ۱۶ کاراکتر. سپس دو رشتهٔ کدگذاریشده به هم پیوسته میشوند تا ULID نهایی ۲۶کاراکتری ساخته شود.
کدگذاری با خواندن عدد در گروههای ۵ بیتی انجام میشود، زیرا هر کاراکتر Base32 یکی از ۳۲ مقدار ممکن را نشان میدهد (2^5 = ۳۲). ده کاراکتر میتوانند حداکثر ۵۰ بیت را در خود جای دهند که برای مهر زمانی ۴۸بیتی کافی است. شانزده کاراکتر دقیقاً ۸۰ بیت را در خود جای میدهند؛ همان اندازهٔ بخش تصادفی، بدون باقیمانده.
مثال حلشده
فرض کنید یک تولیدکنندهٔ ULID در مهر زمانی 1712345678901 (میلیثانیه از مبدأ) اجرا شود و بایتهای تصادفی [12, 240, 88, 3, 199, 45, 6, 231, 128, 17] را دریافت کند.
مرحلهٔ ۱ – کدگذاری مهر زمانی. 1712345678901 را بهطور پیاپی بر ۳۲ تقسیم کنید، هر بار باقیمانده را بردارید و ارقام را از آخر به اول بخوانید (این روش استاندارد تبدیل مبناست). نتیجه، رشتهای ۱۰کاراکتری بهشکل 01HTQW311N است.
مرحلهٔ ۲ – کدگذاری بخش تصادفی. ۱۰ بایت تصادفی را در جریانی شامل ۸۰ بیت قرار دهید، سپس هر بار ۵ بیت را بخوانید و هر قطعهٔ ۵بیتی را به یک کاراکتر Base32 تبدیل کنید. نتیجه، رشتهای ۱۶کاراکتری بهشکل 1KR5G0Y75M3EF00H است.
مرحلهٔ ۳ – پیوستن بخشها. ULID نهایی چنین است:
101HTQW311N 1KR5G0Y75M3EF00H
2بدون فاصله: 01HTQW311N1KR5G0Y75M3EF00H، در مجموع ۲۶ کاراکتر.
استفاده از این تولیدکننده
تولیدکننده در مرورگر اجرا میشود. هر ULID از ساعت مرورگر و ۸۰ بیت تصادفی جدیدی ساخته میشود که از مولد اعداد تصادفی رمزنگاری مرورگر گرفته شده است؛ بنابراین هیچ شناسهای به سرور ارسال نمیشود یا از سرور دریافت نمیگردد.
- تعداد ULIDها یک عدد صحیح از ۱ تا ۵۰ را میپذیرد. مقدار پیشفرض ۱ است.
- تولید ULID یک دستهٔ جدید تولید میکند. تغییر تعداد نیز یک دستهٔ جدید ایجاد میکند.
- گزینهٔ کپی در کنار یک نتیجه، همان ULID را کپی میکند. وقتی بیش از یک مورد نمایش داده شود، گزینهٔ کپی همه کل فهرست را، هر ULID در یک خط، کپی میکند.
- بازنشانی تعداد را به ۱ برمیگرداند و یک ULID جدید تولید میکند.
در زیر نتایج، پنل ساختار ULID نخستین ULID دسته را به دو بخش تقسیم میکند: ۱۰ کاراکتر نخست (مهر زمانی) و ۱۶ کاراکتر پایانی (بخش تصادفی).
ULID در برابر UUID
| ویژگی | ULID | UUID (نسخهٔ ۴) |
|---|---|---|
| طول | ۲۶ کاراکتر | ۳۶ کاراکتر (با خط تیره) |
| قابل مرتبسازی بر اساس زمان ایجاد | بله | خیر |
| کدگذاری | Base32 کراکفورد | هگزادسیمال |
| دارای مهر زمانی | بله (۴۸ بیت) | خیر |
| ایمن برای URL بدون نیاز به اِسکیپ | بله | بله |
هر دو قالب با هدف تولید شناسههایی طراحی شدهاند که بدون وجود مرجع مرکزی برای تخصیص شماره، در سراسر سیستمها یکتا باشند. تفاوت عملی اصلی در مرتبسازی است: چون ULID با مهر زمانی آغاز میشود، مرتبسازی ULIDها بهصورت متن ساده، آنها را بر اساس زمان نیز مرتب میکند. UUID تصادفی چنین ویژگیای ندارد، زیرا هر بخش آن تصادفی است.
کاربردهای رایج
- کلیدهای اصلی پایگاه داده: ULIDها به پایگاه داده اجازه میدهند ردیفهای جدید را تقریباً به ترتیب درجشدنشان نمایهسازی کند؛ کاری که بسیاری از موتورهای پایگاه داده کارآمدتر از کار با کلیدهای کاملاً تصادفی انجام میدهند.
- سیستمهای توزیعشده: سرورهای جداگانه میتوانند هرکدام مستقل از دیگری و بدون هماهنگی با شمارندهای مرکزی ULID تولید کنند و نتایج همچنان تقریباً بهطور قطعی یکتا باشند.
- گزارشهای رویداد: چون ULIDها بر اساس زمان مرتب میشوند، فهرست شناسههای رویداد میتواند بهعنوان خط زمانی تقریبی نیز عمل کند.
- شناسههای API و فایل: قالب ثابت و ایمن برای URL با ۲۶ کاراکتر، در نشانیهای وب و نام فایلها بهخوبی کار میکند.
قالبهای شناسهٔ مرتبط شامل KSUID است که آن نیز برای مرتبسازی، مهر زمانی را کدگذاری میکند، و شناسههای Snowflake که توییتر و دیگران از آنها استفاده میکنند و مهر زمانی، شناسهٔ ماشین و شمارنده را ترکیب میکنند.
نمونههای کد
هر مثال زیر ULID را به همان روشی میسازد که مشخصات ULID توضیح میدهد: کدگذاری مهر زمانی ۴۸بیتی بهشکل ۱۰ کاراکتر Base32 کراکفورد، کدگذاری ۸۰ بیت تصادفی بهشکل ۱۶ کاراکتر دیگر، و سپس پیوستن آنها.
جاوااسکریپت
1const ENCODING_CHARS = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
2
3function encodeULID(time, randomBytes) {
4 // Timestamp: 48 bits -> 10 characters
5 let timestampStr = '';
6 let t = time;
7 for (let i = 0; i < 10; i++) {
8 timestampStr = ENCODING_CHARS[t % 32] + timestampStr;
9 t = Math.floor(t / 32);
10 }
11
12 // Randomness: 80 bits -> 16 characters
13 let result = timestampStr;
14 let bits = 0;
15 let bitCount = 0;
16 for (let i = 0; i < 10; i++) {
17 bits = (bits << 8) | randomBytes[i];
18 bitCount += 8;
19 while (bitCount >= 5) {
20 bitCount -= 5;
21 result += ENCODING_CHARS[(bits >> bitCount) & 0x1f];
22 }
23 }
24 return result;
25}
26
27function generateULID() {
28 const randomBytes = crypto.getRandomValues(new Uint8Array(10));
29 return encodeULID(Date.now(), randomBytes);
30}
31
32console.log(generateULID());
33پایتون
1import os
2import time
3
4ENCODING_CHARS = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
5
6def encode_ulid(time_ms, random_bytes):
7 # Timestamp: 48 bits -> 10 characters
8 chars = []
9 t = time_ms
10 for _ in range(10):
11 chars.append(ENCODING_CHARS[t % 32])
12 t //= 32
13 timestamp_part = "".join(reversed(chars))
14
15 # Randomness: 80 bits -> 16 characters
16 bits = 0
17 bit_count = 0
18 random_part = []
19 for byte in random_bytes:
20 bits = (bits << 8) | byte
21 bit_count += 8
22 while bit_count >= 5:
23 bit_count -= 5
24 random_part.append(ENCODING_CHARS[(bits >> bit_count) & 0x1F])
25
26 return timestamp_part + "".join(random_part)
27
28def generate_ulid():
29 return encode_ulid(int(time.time() * 1000), os.urandom(10))
30
31print(generate_ulid())
32جاوا
1import java.security.SecureRandom;
2import java.time.Instant;
3
4public class ULIDGenerator {
5 private static final SecureRandom random = new SecureRandom();
6 private static final char[] ENCODING_CHARS =
7 "0123456789ABCDEFGHJKMNPQRSTVWXYZ".toCharArray();
8
9 public static String generateULID() {
10 long timestamp = Instant.now().toEpochMilli();
11 byte[] randomBytes = new byte[10];
12 random.nextBytes(randomBytes);
13
14 // Timestamp: 48 bits -> 10 characters
15 char[] timestampChars = new char[10];
16 long t = timestamp;
17 for (int i = 9; i >= 0; i--) {
18 timestampChars[i] = ENCODING_CHARS[(int) (t % 32)];
19 t /= 32;
20 }
21
22 // Randomness: 80 bits -> 16 characters
23 StringBuilder result = new StringBuilder(new String(timestampChars));
24 long bits = 0;
25 int bitCount = 0;
26 for (byte b : randomBytes) {
27 bits = (bits << 8) | (b & 0xFF);
28 bitCount += 8;
29 while (bitCount >= 5) {
30 bitCount -= 5;
31 result.append(ENCODING_CHARS[(int) ((bits >> bitCount) & 0x1F)]);
32 }
33 }
34 return result.toString();
35 }
36
37 public static void main(String[] args) {
38 System.out.println(generateULID());
39 }
40}
41سؤالات رایج
ULID برای چه کاری استفاده میشود؟ از ULID بهعنوان شناسهای یکتا برای یک ردیف پایگاه داده، یک منبع API یا یک رویداد ثبتشده استفاده میشود؛ در مواردی که مرتبسازی بر اساس زمان ایجاد نیز مفید است.
طول ULID چقدر است؟ طول ULID همیشه ۲۶ کاراکتر است: ۱۰ کاراکتر برای مهر زمانی و ۱۶ کاراکتر برای بخش تصادفی.
ULID از چه کدگذاریای استفاده میکند؟ ULID از الفبای Base32 کراکفورد استفاده میکند که از ارقام ۰–۹ و حروف A تا Z تشکیل شده است؛ حروف I، L، O و U برای جلوگیری از اشتباه با نویسههای دیگر حذف شدهاند.
ULID چه تفاوتی با UUID دارد؟ ULID بر اساس زمان ایجاد مرتب میشود و ۲۶ کاراکتر طول دارد. UUID تصادفی (نسخهٔ ۴) بر اساس زمان مرتب نمیشود و با احتساب خط تیرهها ۳۶ کاراکتر طول دارد.
احتمال برخورد بین دو ULID چقدر است؟ ۸۰ بیت تصادفی، احتمال برخورد را بسیار ناچیز میکند. برای رسیدن به احتمال ۵۰٪ تطابق بین حداقل دو مورد، باید تقریباً ۱٫۳ تریلیون ULID در یک میلیثانیه تولید شود.
آیا میتوان ULIDها را بدون اتصال به اینترنت تولید کرد؟ بله. ULID فقط به ساعت محلی و یک مولد عدد تصادفی نیاز دارد، بنابراین میتوان آن را کاملاً بهصورت آفلاین ایجاد کرد.
منابع
- "ULID Specification." گیتهاب، https://github.com/ulid/spec.
- "Crockford's Base32 Encoding." http://www.crockford.com/base32.html.