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

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

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

مولد ULID

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

التوثيق

ما هو المعرّف ULID؟

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

بنية المعرّف ULID

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

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

ويُكتب الجزآن بأبجدية Crockford's Base32: الأرقام 0-9 والحروف A-Z، مطروحًا منها الحروف I وL وO وU. وقد استُبعدت هذه الحروف الأربعة لأنها قد تُخلط بالرقمين 0 و1، أو يُخلط بعضها ببعض. والنتيجة سلسلة قصيرة يسهل نطقها بصوت عال ويؤمَن وضعها مباشرة في عنوان URL.

كيفية حساب معرّف ULID

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

  1. قراءة الوقت الحالي بالميلي ثانية منذ بداية زمن يونكس. وهذا عدد بطول 48 بت.
  2. توليد 80 بت من البيانات العشوائية، عادةً من مولّد أعداد عشوائية آمن تشفيريًّا، فينتج 10 بايتات عشوائية.
  3. ترميز الجزأين كلٍّ على حدة بأبجدية Crockford's Base32: يصير الطابع الزمني ذو الـ 48 بت 10 أحرف، وتصير القيمة العشوائية ذات الـ 80 بت 16 حرفًا. ثم تُوصل السلسلتان المرمَّزتان لتكوين المعرّف النهائي بطول 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 – ترميز الجزء العشوائي. احزم البايتات العشوائية العشرة في تيار من 80 بت، ثم اقرأ 5 بتات في كل مرة، وحوّل كل كتلة من 5 بتات إلى حرف Base32. فينتج السلسلة 1KR5G0Y75M3EF00H بطول 16 حرفًا.

الخطوة 3 – وصل الجزأين. المعرّف ULID النهائي هو:

101HTQW311N 1KR5G0Y75M3EF00H
2

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

استعمال هذا المولّد

يعمل المولّد داخل المتصفح. ويُبنى كل معرّف ULID من ساعة المتصفح ومن 80 بتًا عشوائيًّا جديدًا مأخوذة من مولّد الأعداد العشوائية التشفيري في المتصفح، فلا يُرسَل أي معرّف إلى خادم ولا يُجلَب منه.

  • عدد معرّفات ULID يقبل عددًا صحيحًا من 1 إلى 50. والقيمة الافتراضية 1.
  • توليد ULID ينتج دفعة جديدة. وتغيير العدد ينتج دفعة جديدة كذلك.
  • نسخ على نتيجة ما ينسخ ذلك المعرّف وحده. وعند عرض أكثر من واحد، ينسخ نسخ الكل القائمة كاملة، معرّفًا واحدًا في كل سطر.
  • إعادة تعيين يعيد العدد إلى 1 ويولّد معرّفًا جديدًا واحدًا.

وتحت النتائج، تقسم لوحة "بنية المعرّف ULID" أول معرّف في الدفعة إلى جزأيه: الأحرف العشرة الأولى (الطابع الزمني) والأحرف الستة عشر الأخيرة (الجزء العشوائي).

ULID مقابل UUID

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

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

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

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

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

أمثلة برمجية

يبني كل مثال أدناه معرّف ULID بالطريقة نفسها التي تصفها مواصفة ULID: ترميز الطابع الزمني ذي الـ 48 بت في 10 أحرف Crockford 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 معرّفًا فريدًا لصف في قاعدة بيانات، أو لمورد في واجهة برمجة، أو لحدث مسجَّل، في الحالات التي يفيد فيها الترتيب بوقت الإنشاء أيضًا.

ما طول المعرّف ULID؟ طول المعرّف ULID 26 حرفًا دائمًا: 10 أحرف للطابع الزمني و16 للجزء العشوائي.

ما الترميز الذي يستعمله المعرّف ULID؟ يستعمل المعرّف ULID أبجدية Crockford's Base32، المكوّنة من الأرقام 0-9 والحروف A-Z بعد حذف I وL وO وU تجنبًا للخلط مع حروف أخرى.

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

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

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

المراجع

  1. "ULID Specification." GitHub, https://github.com/ulid/spec.
  2. "Crockford's Base32 Encoding." http://www.crockford.com/base32.html.