تخطي إلى المحتوى

مولد ULID - إنشاء معرفات فريدة قابلة للفرز مجانًا عبر الإنترنت

أداة مولد ULID المجانية تنشئ معرفات فريدة وقابلة للفرز على الفور. قم بإنشاء معرفات ULID آمنة تشفيريًا لقواعد البيانات وواجهات برمجة التطبيقات والأنظمة الموزعة عبر الإنترنت.

مولد ULID

أدخل قيمة لعرض النتائج
حاسبة التحميل...
📚

التوثيق

ما هو ULID؟

ULID (المعرّف الفريد عالميًا القابل للفرز معجميًا) رمز من 26 حرفًا يُستخدم لتمييز السجلات في قاعدة بيانات أو حدث في نظام موزّع. وهو يجمع بين الوقت الحالي وبيانات عشوائية، فتنتهي قائمة معرّفات ULID المرتبة أبجديًا مرتبةً أيضًا بحسب وقت إنشاء كل منها. ومولّد ULID أداة تُنتج هذه الرموز.

بنية ULID

يتكوّن ULID من جزأين يُكتبان أحدهما بعد الآخر دون فاصل:

  • الطابع الزمني (10 أحرف): عدد المللي ثانية منذ 1 يناير 1970 (حقبة يونكس)، ويُخزَّن على هيئة عدد صحيح من 48 بتًّا.
  • العشوائية (16 حرفًا): 80 بتًّا من البيانات العشوائية، أُضيفت لكي يظل معرّفان ULID أُنشئا في المللي ثانية نفسها مختلفين على الأرجح تمامًا.

يُكتب الجزآن كلاهما بأحرف أبجدية Base32 لكروكفورد: الأرقام 0-9 والأحرف A-Z باستثناء الأحرف I وL وO وU. فقد يُخلط بين I وL والرقم 1، وبين O والرقم 0. أما U فتُستبعد لتفادي تكوّن كلمات بذيئة عن غير قصد. والنتيجة سلسلة قصيرة يسهل نطقها بصوت عالٍ ويمكن وضعها في عنوان URL مباشرة بأمان.

كيفية حساب ULID

يبني مولّد ULID المعرّف في ثلاث خطوات.

  1. قراءة الوقت الحالي بالمللي ثانية منذ حقبة يونكس. وهو عدد من 48 بتًّا.
  2. توليد 80 بتًّا من البيانات العشوائية، عادةً من مولّد أعداد عشوائية آمن تشفيريًا، فينتج 10 بايتات عشوائية.
  3. ترميز الجزأين كلًّا على حدة بأبجدية Base32 لكروكفورد: يصبح الطابع الزمني ذو 48 بتًّا 10 أحرف، وتصبح القيمة العشوائية ذات 80 بتًّا 16 حرفًا. ثم تُضم السلسلتان المرمّزتان لتكوّنا ULID النهائي المكوّن من 26 حرفًا.

يعمل الترميز بقراءة العدد 5 بتات في كل مرة، لأن كل حرف في Base32 يمثل واحدة من 32 قيمة ممكنة (2^5 = 32). وتتسع عشرة أحرف لما يصل إلى 50 بتًّا، وهذا يكفي للطابع الزمني ذي 48 بتًّا. وتتسع ستة عشر حرفًا لـ80 بتًّا بالضبط، وهذا يطابق جزء العشوائية دون أي فائض.

مثال تطبيقي

لنفترض أن مولّد ULID يعمل عند الطابع الزمني 1712345678901 (بالمللي ثانية منذ الحقبة) ويسحب البايتات العشوائية [12, 240, 88, 3, 199, 45, 6, 231, 128, 17].

الخطوة 1 – ترميز الطابع الزمني. اقسم 1712345678901 على 32 مرارًا، مع أخذ الباقي في كل مرة وقراءة الأرقام من الأخير إلى الأول (وهذا تحويل أساس معتاد). ينتج عن ذلك السلسلة 01HTQW311N المكوّنة من 10 أحرف.

الخطوة 2 – ترميز العشوائية. ضع البايتات العشوائية البالغ عددها 10 في تيار من 80 بتًّا، ثم اقرأ 5 بتات في كل مرة، محوّلًا كل مقطع من 5 بتات إلى حرف Base32. ينتج عن ذلك السلسلة 1KR5G0Y75M3EF00H المكوّنة من 16 حرفًا.

الخطوة 3 – ضم الجزأين. يكون ULID النهائي كما يلي:

101HTQW311N 1KR5G0Y75M3EF00H
2

مكتوبًا دون المسافة: 01HTQW311N1KR5G0Y75M3EF00H، أي 26 حرفًا في المجموع.

ULID مقابل UUID

الخاصيةULIDUUID (الإصدار 4)
الطول26 حرفًا36 حرفًا (مع الواصلات)
قابلة للفرز بحسب وقت الإنشاءنعملا
الترميزBase32 لكروكفوردست عشري
يحتوي على طابع زمنينعم (48 بتًّا)لا
آمن في عناوين URL دون ترميز خاص للأحرفنعمنعم

تهدف الصيغتان كلتاهما إلى إنتاج معرّفات فريدة عبر الأنظمة دون سلطة مركزية توزّع الأرقام. والفرق العملي الرئيسي هو الفرز: فبما أن ULID يبدأ بطابع زمني، فإن فرز معرّفات ULID كنص عادي يرتّبها بحسب الوقت أيضًا. أما UUID العشوائي فلا يملك هذه الخاصية، لأن كل جزء منه عشوائي.

الاستخدامات الشائعة

  • المفاتيح الأساسية في قواعد البيانات: تتيح معرّفات ULID لقاعدة البيانات فهرسة الصفوف الجديدة تقريبًا بترتيب إدخالها، وهو ما تتعامل معه محركات قواعد البيانات الكثيرة بكفاءة أكبر من المفاتيح العشوائية تمامًا.
  • الأنظمة الموزّعة: يمكن لكل خادم أن يولّد معرّفات ULID بصورة مستقلة دون التنسيق مع عدّاد مركزي، وتظل النتائج فريدة على الأرجح تمامًا.
  • سجلات الأحداث: بما أن معرّفات ULID تُفرز بحسب الوقت، يمكن أن تؤدي قائمة معرّفات الأحداث دور جدول زمني تقريبي.
  • معرّفات واجهات API والملفات: الصيغة الثابتة المكوّنة من 26 حرفًا والآمنة في عناوين URL تعمل بسلاسة في عناوين الويب وأسماء الملفات.

ومن صيغ المعرّفات ذات الصلة KSUID، الذي يرمّز أيضًا طابعًا زمنيًا لأغراض الفرز، ومعرّفات Snowflake التي تستخدمها تويتر وغيرها، وتجمع بين طابع زمني ومعرّف للجهاز وعدّاد.

أمثلة برمجية

يبني كل مثال أدناه معرّف ULID بالطريقة نفسها التي تصفها مواصفة ULID: ترميز الطابع الزمني ذي 48 بتًّا في 10 أحرف من Base32 لكروكفورد، وترميز 80 بتًّا من العشوائية في 16 حرفًا أخرى، ثم ضمّهما.

JavaScript

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

Python

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

Java

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 26 حرفًا دائمًا: 10 أحرف للطابع الزمني و16 حرفًا للجزء العشوائي.

ما الترميز الذي يستخدمه ULID؟ يستخدم ULID أبجدية Base32 لكروكفورد، وتتألف من الأرقام 0-9 والأحرف A-Z مع حذف I وL وO وU لتفادي سوء القراءة والكلمات غير المقصودة.

ما الفرق بين ULID وUUID؟ يُفرز ULID بحسب وقت الإنشاء وطوله 26 حرفًا. أما UUID العشوائي (الإصدار 4) فلا يُفرز بحسب الوقت وطوله 36 حرفًا مع الواصلات.

ما احتمال حدوث تصادم بين معرّفين من ULID؟ تجعل 80 بتًّا من العشوائية التصادم غير مرجح للغاية. ويلزم توليد نحو 1.3 تريليون معرّف ULID في المللي ثانية نفسها للوصول إلى احتمال قدره 50% لتطابق أي معرّفين.

هل يمكن توليد معرّفات ULID دون اتصال بالإنترنت؟ نعم. يحتاج ULID فقط إلى الساعة المحلية ومولّد أعداد عشوائية، لذا يمكن إنشاؤه دون اتصال بالكامل.

المراجع

  1. مشروع ULID. Universally Unique Lexicographically Sortable Identifier. ULID specification — طابع زمني بالمللي ثانية من 48 بتًّا، و80 بتًّا من العشوائية، وصيغة معيارية من 10 + 16 حرفًا، وأبجدية Base32 لكروكفورد.
  2. دوغلاس كروكفورد. Base 32. Crockford's Base32 — الأبجدية المكوّنة من 32 رمزًا وسبب استبعاد I وL وO وU.