---
title: "وداعًا للإدراج العشوائي: UUIDv7 مقابل ULID مقابل UUIDv4 — المعرّفات الفريدة والأداء وأثرها على قواعد البيانات"
description: "مفاتيح UUIDv4 العشوائية تضاعف حجم جداولك في صمت وتدمّر أداء الإدراج. كيف تعالج المعرّفات المرتبة زمنيًا — ULID ومعيار UUIDv7 الجديد — مشاكل التجزئة ومحلية الـ cache وعمليات القرص."
url: https://akemara.com/ar/blog/uuidv7-ulid-uuidv4/
lang: ar
author: "أحمد كمال عمارة"
date: 2025-10-08
lastmod: 2025-10-08
section: blog
tags: ["قواعد البيانات","الأنظمة الموزّعة","معمارية البرمجيات","هندسة البرمجيات","UUID"]
translations:
  en: https://akemara.com/en/blog/uuidv7-ulid-uuidv4/
---

# وداعًا للإدراج العشوائي: UUIDv7 مقابل ULID مقابل UUIDv4 — المعرّفات الفريدة والأداء وأثرها على قواعد البيانات

> مفاتيح UUIDv4 العشوائية تضاعف حجم جداولك في صمت وتدمّر أداء الإدراج. كيف تعالج المعرّفات المرتبة زمنيًا — ULID ومعيار UUIDv7 الجديد — مشاكل التجزئة ومحلية الـ cache وعمليات القرص.

## مقدمة {#introduction}

المعرّفات الفريدة هي ما يجعل نماذج البيانات تعمل في التطبيقات الحديثة. واختيار الصيغة الصحيحة لها يؤثر على الأداء وقابلية التوسع، سواء كانت مفاتيح أساسية (Primary Keys) في قاعدة بيانات أو معرّفات في نظام موزّع. حظيت **معرّفات UUID (المعرّفات الفريدة عالميًا)** بشعبية طويلة لأنها فريدة على مستوى العالم دون الحاجة إلى منسّق مركزي. و**الإصدار الرابع ‏(UUIDv4)**، وهو معرّف من 128 بت يولَّد عشوائيًا، من أكثرها شيوعًا لبساطته وضآلة احتمال تصادمه مع معرّف آخر. لكن للعشوائية الخالصة عيوبًا في بيئات قواعد البيانات، خصوصًا في الفهرسة والأداء. أما المخططات الأحدث مثل **ULID (المعرّف الفريد القابل للفرز معجميًا)** و**الإصدار السابع ‏(UUIDv7)** فتُضمّن عنصر الوقت في المعرّف، ما يجعل فرز المعرّفات معجميًا (أي زمنيًا) ممكنًا ويعالج مشاكل UUIDv4. في هذا المقال سنستعرض الفروق بين UUIDv4 وULID وUUIDv7، ونناقش مشاكل UUIDv4 في قواعد البيانات وكيف يعالجها ULID، وكيف يقارن ULID بالمعيار الجديد UUIDv7. وسنتناول أيضًا استخدامات كل منها وأثرها على قواعد البيانات (بتركيز على MySQL/MariaDB وPostgreSQL)، بما يشمل الأداء وسلوك الفرز والإنتروبيا وقضايا عملية أخرى.

## UUIDv4: عشوائية خالصة وتحدياتها {#uuidv4-pure-randomness-and-its-challenges}

**UUIDv4** معرّف من 128 بت منها 122 بت عشوائية، وتُستخدم البتات المتبقية لتحديد الإصدار والمتغيّر (Variant). يُكتب عادة بـ 32 خانة سداسية عشرية على نمط 8–4–4–4–12، مثل 7f750c37–4d0b-4f0b-b922–37be0b9138a7. من أبرز مزاياه أنه يعتمد على بايتات عشوائية فقط مع خطر تصادم شبه معدوم (يكاد يكون صفرًا لمعظم التطبيقات، بوجود 2¹²² احتمالًا). لذا فهو ممتاز للأنظمة الموزّعة: أي عقدة تستطيع توليد معرّفاتها باستقلال، واحتمال أن تولّد عقدتان المعرّف نفسه ضئيل للغاية. وهو جيد أيضًا عندما تحتاج إلى عدم القابلية للتنبؤ (كرموز الجلسات والمعرّفات الحساسة أمنيًا) لأن العشوائية تمنع تخمين المعرّفات بالتسلسل.

**المشكلة** تظهر عندما يعمل UUIDv4 كمفتاح أساسي أو فهرس داخل قاعدة البيانات. فبما أن قيمه عشوائية تقريبًا، لا تتبع الإدراجات الجديدة أي ترتيب طبيعي. تستخدم معظم قواعد البيانات العلائقية فهرس B-Tree للمفاتيح الأساسية، وعند إدراج قيم عشوائية قد تحطّ كل قيمة جديدة في موضع عشوائي من الفهرس. يسبب هذا تجزئة الفهرس (Index Fragmentation): تنقسم صفحات البيانات كثيرًا وتبقى نصف فارغة، وتتبعثر السجلات على القرص. استخدام UUID عشوائي كمفتاح أساسي في محرك InnoDB لدى MySQL (ومحرك MariaDB الذي يعمل بالطريقة نفسها) قد يُهوي بالأداء لأن المحرك يُجمّع الجدول فيزيائيًا حسب المفتاح الأساسي. يتوقع InnoDB مفاتيح أساسية مرتّبة ويتحسّن بملء الصفحات إلى نحو 94%. أما مع الإدراج العشوائي فقد لا تمتلئ الصفحات إلا إلى نحو 50% قبل الانقسام. لذلك قد يتضخم جدول بمفاتيح UUID عشوائية إلى ضعف حجم البيانات نفسها تقريبًا مقارنة بمفاتيح تسلسلية، لكثرة الصفحات الممتلئة جزئيًا. أظهر مثال واقعي من Percona أن جدولًا بمليار صف ومفتاح أساسي UUIDv4 من نوع CHAR(36) بلغ حجمه نحو 1 تيرابايت، وبعد إعادة تنظيمه بمفتاح مرتّب تقلص إلى نحو 450 غيغابايت — أي النصف. لقد سببت الإدراجات العشوائية «تجزئة متطرفة»: صفحات فهرس جدول الـ UUID كانت ممتلئة بنحو 50% فقط، مقابل نحو 90% لمفتاح تسلسلي أو UUID مرتّب.

لعشوائية UUIDv4 آثار كبيرة على الأداء:

- **تجزئة الفهرس وانقسام الصفحات:** عند إضافة عناصر عشوائية، يضطر الـ B-Tree إلى شق الصفحات من منتصفها بدلًا من الإلحاق في النهاية. كثرة الانقسامات تخلّف صفحات نصف فارغة و«فهرسًا متضخمًا قليل الكثافة»، فيتباطأ معدل الإدراج ويُستهلك القرص. ومع البيانات الضخمة سيتحمل MySQL بمفتاح UUID عشوائي عبء صيانة فهرس أكبر يبطئ الكتابة. وكما يوضح أحد الشروح: عندما تتزايد القيم يمكن إلحاق صفحة جديدة في النهاية، أما مع القيم العشوائية فينتج الانقسام صفحتين نصف ممتلئتين تبقيان كذلك طويلًا.
- **محلية الـ Cache:** الوصول إلى السجلات المضافة حديثًا أقل ودية للـ cache لأن سجلات UUIDv4 متناثرة عبر الفهرس. لا يستطيع الـ buffer الاحتفاظ بالإدراجات «الحديثة» متجاورة لأن «الحديث» في كل مكان. في المقابل، مع الإدراج التسلسلي تتركز أحدث البيانات في عدد صغير من الصفحات المتجاورة الساخنة في الذاكرة. مع UUIDv4 تتشتت مجموعة العمل فتكثر إخفاقات الـ cache، وقد تضطر قاعدة البيانات لتحميل صفحات كثيرة متفرقة للتعامل مع بيانات حديثة. وباستخدام مفتاح تسلسلي يقل الوصول للقرص في أنماط الاستخدام المعتادة في MySQL وPostgreSQL.
- **عمليات القرص (Disk I/O):** بسبب عشوائية UUIDv4، لا يستطيع محرك التخزين الإلحاق فحسب؛ بل عليه البحث المستمر عن الصفحات وإعادة ترتيبها. فترتفع عمليات القرص في الأحمال كثيفة الإدراج، ويجري InnoDB كتابات وانقسامات كثيرة في منتصف الفهرس. للتطبيقات كثيفة الكتابة، المفتاح التسلسلي أرفق بالقرص بكثير، وتفادي نمط الإدراج العشوائي يمنح غالبًا معدلات إدراج أسرع بكثير. وقد وجد باحثون أن المعرّفات المرتبة زمنيًا مثل UUIDv7 أو ULID «تقلص عبء عمليات القرص للإدراج بشكل كبير» مقارنة بـ UUIDv4.
- **تخزين أكبر وعبء فهارس أثقل:** معرّفات UUID كبيرة (16 بايت) ما يفاقم التجزئة، وتستهلك أكثر (36 بايت وزيادة) عند تخزينها نصًا (سلسلة من 36 حرفًا). عمود INT تلقائي الزيادة في MySQL يستهلك 4 بايتات، بينما UUID الثنائي 16 بايت (أكبر 4 مرات) وUUID النصي 36 بايت (أكبر 9 مرات من INT). وتتضمن كل الفهارس الثانوية قيمة المفتاح الأساسي، فكلما كبر المفتاح كبرت الفهارس الثانوية. جميع متغيّرات UUID (v4 وv7 وULID وغيرها) 128 بت، والأفضل تخزينها بصيغة ثنائية مدمجة من 16 بايت. تستطيع دالة UUID_TO_BIN()‎ في MySQL مع علم التبديل تخزين v1/v2 بترتيب زمني في 16 بايت. لا يوجد ترتيب كامن يمكن استغلاله في v4، لكن binary(16)‎ يبقى أصغر من char(36)‎.

إجمالًا، **UUIDv4** ممتاز للتفرد وسهل التوليد، لكنه بلا أي عنصر قابل للفرز، ما يجعله «غير كفء في المواقف التي يكون فيها الفرز حاسمًا للأداء الأمثل، مثل مفاتيح قواعد البيانات». وقد اكتشفت فرق كثيرة أن استخدامه مفتاحًا أساسيًا يبطئ الإدراج ويضخّم الفهرس مع الوقت. تجربة PostgreSQL أفضل قليلًا من MySQL لأن PostgreSQL لا يُجمّع الجدول حسب المفتاح الأساسي افتراضيًا (كومة الجدول منفصلة)، فالمفاتيح العشوائية تؤذي صفحات الفهرس أساسًا لا الترتيب الفيزيائي للصفوف. ومع ذلك ستعاني فهارس B-Tree في PostgreSQL من انقسام الصفحات وامتلائها الجزئي نفسه مع البيانات العشوائية. باختصار: علة UUIDv4 أنه عشوائي بالكامل، فيحرم قاعدة البيانات من المحلية.

## ULID: معرّفات قابلة للفرز بطابع زمني مدمج {#ulid-sortable-identifiers-with-embedded-timestamp}

ابتكر Alizain Feerasta **الـ ULID (المعرّف الفريد القابل للفرز معجميًا)** عام 2016 لعلاج مشاكل الـ UUID العشوائي. الـ ULID أيضًا معرّف من 128 بت، لكن بنيته تختلف تمامًا عن UUIDv4: أول **48 بت طابع زمني** (ميلي ثانية منذ بداية Unix)، وآخر **80 بت عشوائية**. بهذا التصميم يحتفظ الـ ULID بالعشوائية الضامنة للتفرد العالمي، ويكتسب في الوقت نفسه بعدًا ترتيبيًا: إذا ولّدت ULIDs عبر الزمن، فإن ترتيبها المعجمي (عند مقارنتها كسلاسل أو بايتات) يعكس وقت إنشائها. بعبارة أخرى: تُفرز الـ ULIDs زمنيًا بدقة الميلي ثانية. لا يُعرض الـ ULID القياسي بالست عشري بل بترميز Base32 لكروكفورد، فينتج سلسلة من 26 حرفًا (مثل 01H9AZ98MT1XY7TK3XN9B0AJP3). لا يتضمن هذا الترميز حروفًا ملتبسة (مثل I وL وO وU) ولا يفرّق بين حالة الأحرف، ما يجعل الـ ULIDs أسهل قراءة ونسخًا ووضعًا في الروابط. وسلسلة الـ 26 حرفًا أقصر وأوضح من سلسلة UUID ذات الـ 36 حرفًا (بالشرطات) أو الـ 32 خانة (بدونها).

**كيف يحل ULID مشاكل UUIDv4:** بدمج الوقت في المعرّف، يضمن الـ ULID أن المعرّفات المولّدة بترتيب زمني ستُفرز بالترتيب الزمني نفسه. ولهذا فوائد فورية لقواعد البيانات:

- **نمط إدراج شبه إلحاقي:** عند استخدام ULIDs كمفاتيح أساسية، تحمل الإدراجات الجديدة عادة طوابع زمنية أكبر (أحدث)، فتُدرج الصفوف الجديدة عادة في «نهاية» الفهرس، وإن جاز أن تختل قليلًا مع فروق الساعات. يصبح سلوك الفهرس أقرب كثيرًا إلى مفتاح تسلسلي أو تلقائي الزيادة، فتقل انقسامات الصفحات والتجزئة بشدة. استخدام مفتاح مرتّب زمنيًا مثل ULID أو UUIDv7 في MySQL/InnoDB «ينتج انقسامات صفحات أقل وتحسنًا عامًا في كفاءة التخزين». وتبقى البيانات متجمعة حسب وقت إنشائها، وهو مفيد لتطبيقات كثيرة كسجلات الأحداث. يغدو الإدراج أكثر قابلية للتنبؤ، ويُخدم غالبًا بإلحاق صفحات جديدة في النهاية بدل شق القديمة.
- **محلية Cache أفضل:** يجعل الـ ULID المعرّفات المضافة حديثًا متقاربة القيم، فتتركز أحدث الصفوف في جزء صغير من الفهرس. تستطيع قاعدة البيانات إبقاء نهاية الفهرس (أحدث الصفحات) في الذاكرة، فترتفع إصابات الـ cache للإدراجات والاستعلامات الحديثة. كان UUIDv4 يوزّع الصفوف الجديدة في كل مكان، أما ULID فيبقيها مرتبة زمنيًا فيتحسن التخزين المؤقت. مثلًا، استعلام عن أحدث N سجل يمسح نطاقًا واحدًا من الفهرس دون قفزات.
- **فرز زمني طبيعي:** يتيح لك الـ ULID الفرز بالمعرّف نفسه للحصول على الترتيب الزمني، فقد تستغني في بعض الحالات عن عمود طابع زمني مخصص للفرز. إذا كان مفتاحك الأساسي ULID مثلًا، فإن ORDER BY id DESC يعيد الأحدث أولًا لأن أكبر ULID يقابل أحدث طابع زمني. يبسّط هذا منطق التطبيق أو الفهرسة، لكن تذكّر أنك إن احتجت معلومات تاريخ/وقت كاملة أو استعلامات بالتاريخ، فالأرجح أنك ستبقي عمود طابع زمني عادي للراحة. الجوهر أن ULID يشفّر الوقت، وهذا نافع جدًا للسجلات وبيانات السلاسل الزمنية حيث يهم الاسترجاع مرتّبًا.
- **رتابة داخل الميلي ثانية الواحدة:** ماذا لو وُلّد أكثر من ULID في الميلي ثانية نفسها؟ ستتطابق بتات الطابع الزمني. تنص مواصفة ULID عندئذٍ على استخدام زيادة رتيبة (Monotonic) في الجزء العشوائي: إذا استُدعي المولّد أكثر من مرة في أقل من ميلي ثانية، يلاحظ ثبات الطابع الزمني فيضيف 1 إلى الجزء العشوائي لكل معرّف جديد. يضمن هذا بقاء ULIDs المشتركة في الطابع الزمني مرتّبة (المولَّد ثانيًا له جزء عشوائي أكبر بواحد، فيصبح أكبر معجميًا). مثلًا، إن خرج اثنان في الميلي ثانية نفسها فقد ترى 01BX5ZZKBKACTAV9WEVGEMMVS0 يليه 01BX5ZZKBKACTAV9WEVGEMMVS1 — تغيّر الحرف الأخير من 0 إلى 1. تمنع هذه الخطوة التكرارات داخل الطابع الواحد وتحفظ الترتيب. وفي الأنظمة الفعلية، احتمال الحاجة إلى أكثر من 2⁸⁰ معرّفًا في ميلي ثانية شبه معدوم (فذلك 1.2e24، أي أضعاف ملايين المعرّفات في الثانية)، فلا قلق من الفيضان.
- **التصادم والإنتروبيا:** لدى الـ ULID عشوائية من 80 بت، أقل قليلًا من 122 بت في UUIDv4 لكنها هائلة. يمنح الجزء العشوائي 1.2×10²⁴ احتمالًا في كل ميلي ثانية حتى مع تكرر الطابع الزمني. يبقى احتمال التصادم شبه صفري لأي استخدام واقعي (للمقارنة: توليد 2⁴⁰ ≈ تريليون ULID يعطي احتمال تصادم نحو 1 من 2⁴⁰، وهو ضئيل جدًا). ووجدت إحدى الدراسات أن للـ ULIDs «احتمالات تصادم مهملة حتى عند معدلات توليد عالية»، بل إنها أقل عرضة للتصادم من UUIDv7 ذي الـ 74 بت العشوائية. باختصار: الـ ULIDs فريدة عالميًا كما UUIDv4 تمامًا.
- **سهولة القراءة:** يقال كثيرًا إن الـ ULIDs أسهل قراءة للبشر. لا تستخدم أبجدية Base32 لكروكفورد حروفًا متشابهة، فسلسلة الـ 26 حرفًا (مثل 01GZ9TGXKJQR6Z1YT1BP7FY4WX) أسلس في التعامل من UUID من 36 حرفًا بست عشري مختلط وشرطات. ولا تفرّق الـ ULIDs بين حالة الأحرف، ما يجنّبك أخطاء التعامل اليدوي (لا حاجة لحفظ الحالة كما في الست عشري). وهي آمنة في الروابط لاقتصارها على حروف وأرقام. تفيد هذه الخصائص إذا كنت ستعرض المعرّفات أو تسجّلها حيث قد يقرؤها أو يكتبها أحد. لكن ثمة مقايضة: يمكنك تخزين الـ ULIDs نصًا (26 حرفًا) أو ثنائيًا (16 بايت). مقارنة السلاسل أبطأ قليلًا من مقارنة البايتات الخام، والصيغة النصية أكبر (26 بايت مقابل 16). وبعض قواعد البيانات، كـ Postgres، لديها نوع UUID لقيم 16 بايت لكن لا نوع أصلي للـ ULID؛ يمكنك تخزينه في CHAR(26)‎ أو ثنائيًا (BYTEA بعد تحويل Base32 إلى بايتات). وتوفر مكتبات ULID كثيرة الصيغتين: الثنائية الخام والسلسلة المرمّزة.

خلاصة القول: تجعل الـ ULIDs المعرّفات الفريدة عالميًا أسهل استخدامًا وأسرع وصولًا في قواعد البيانات المتعاملة مع بيانات زمنية. وقد أصبحت معيارًا واقعيًا في أنظمة كثيرة تريد فوائد UUID دون ضريبة أداء المفاتيح العشوائية بالكامل. الجانب السلبي أن ULID ليس (بعد) معيارًا رسميًا من IETF أو ISO، ولا تدعمه كل اللغات وقواعد البيانات، فستستخدم غالبًا مكتبة لتوليده. الخبر الجيد أن ثمة تطبيقات بلغات كثيرة — Java وGo وPython وJavaScript — ويمكنك عند الحاجة تنفيذ المواصفة بنفسك بسهولة. وتذكّر أيضًا أن الـ ULIDs، كأي معرّف زمني، تكشف الطابع الزمني: من يرى ULID يستطيع فك أول 10 حروف لمعرفة لحظة إنشائه بدقة. لا يمثل هذا مشكلة في حالات كثيرة (بل قد يفيد)، لكن إن كنت لا تريد إفشاء عمر السجلات أو ترتيبها (لأسباب أمنية أو خصوصية) فقد لا يكون المخطط الزمني خيارك الأفضل. سنتوسع في هذا بقسم المقارنات.

## UUIDv7: معيار UUID الجديد المرتّب زمنيًا {#uuidv7-the-new-time-ordered-uuid-standard}

**UUIDv7** عضو جديد في عائلة UUID، قُنّن في RFC 9562 (مايو 2024)، الذي حلّ محل RFC 4122، كمتغيّر UUID مرتّب زمنيًا. وقد استلهم كثيرًا من نجاح ULID. يرسّخ UUIDv7 فكرة «طابع زمني + بتات عشوائية» داخل معيار UUID نفسه. فهو مثل الـ ULID: 128 بت يتصدرها طابع زمني Unix من 48 بت (بالميلي ثانية) في البتات الأعلى، والبقية عشوائية مع إمكانية إضافة عداد رتيب لمنع التكرار داخل الميلي ثانية الواحدة. يخصص UUIDv7 للطابع الزمني 48 بت، ولرقم الإصدار 4 بت (وهي 0111 للإصدار السابع)، والباقي (128−48−4 = 76 بت) مزيج من العشوائية وبتات عداد اختيارية. وبعد حجز بتات مؤشر «المتغيّر» في UUID، يتبقى نحو 74 بت إنتروبيا خالصة. المتغيّر هو أهم بتّين في بايت يدل على UUID وفق المعيار، ويُضبطان للإصدار 7 على النمط القياسي 10xx. هكذا تقل عشوائية UUIDv7 قليلًا عن 80 بت لدى ULID، لكن الفضاء يظل هائلًا: نحو 2⁷⁴ معرّفًا فريدًا في كل ميلي ثانية، مثل ULID عمليًا. وإن احتجت أكثر، تنص المواصفة على استخدام عداد وترحيل الطابع الزمني عند اللزوم — لكن واقعيًا لن يُبلغ 2⁷⁴ (1.8e22) في الميلي ثانية أبدًا. والتصادمات شبه منعدمة؛ وجدت إحدى الدراسات أن خطر التصادم مع UUIDv7 حتى عند معدلات عالية جدًا ضئيل للغاية (وأعلى قليلًا فقط من ULID).

**الميزة الرئيسة لـ UUIDv7** أنه يمنحك UUID موحّدًا للجميع ومرتّبًا زمنيًا: فوائد ULID (تسلسل زمني وأداء أفضل لقاعدة البيانات) لكن بصيغة تتوافق مع أدوات وأنواع UUID القائمة. يُكتب UUIDv7 قياسيًا كسلسلة ست عشرية من 36 حرفًا بشرطات؛ مثلًا قد يكون 0199c376-ce80–7b08–81fd-5524d4a20ed1 معرّف UUIDv7 — الرقم 7 في المجموعة الثالثة يدل على الإصدار، والمقطع الأول 0199c376ce80 طابع زمني (0x0199c376ce80). وبما أن الطابع الزمني في البتات العليا، فإن **فرز UUIDv7 معجميًا (أو مقارنته كبايتات خام)** يطابق الترتيب الزمني. إن وضعت قيم UUIDv7 في عمود UUID لدى PostgreSQL أو BINARY(16)‎ لدى MySQL، فترتيبها البايتي الطبيعي مرتّب زمنيًا — أي أن الفهرس التصاعدي على العمود سيرتب صفوفك زمنيًا تلقائيًا. وهي الخاصية نفسها لدى ULID، لكن بترميز كتابي مختلف.

ومن **منظور قواعد البيانات**، يعمل UUIDv7 كثيرًا مثل ULID:

- **إدراجات مرتّبة:** تحمل قيم UUIDv7 الجديدة عادة مكوّن طابع زمني متزايدًا (ما لم تنحرف الساعة أو تُولَّد القيم خارج الترتيب)، فتتجه الإدراجات غالبًا إلى «نهاية» الفهرس. يمنحك هذا تجزئة منخفضة ومعامل ملء صفحات مرتفعًا، مثل ULID تمامًا. ومن حيث كفاءة استغلال صفحات الفهرس، سيعامل InnoDB في MySQL مفتاح UUIDv7 الأساسي معاملة قريبة جدًا من AUTO_INCREMENT. ولتفادي مشاكل انقسام الصفحات، يوصي فريق PlanetScale بشدة باستخدام «معرّفات UUID زمنية مثل الإصدار 6 أو 7». ويستفيد PostgreSQL كذلك من المحلية الأفضل فيقل احتياجه لإعادة موازنة الفهرس عبر الزمن.
- **الأداء:** عمليًا، وجد كثيرون أن UUIDv7 وULID يقدمان أداء إدراج واستعلام أفضل من UUIDv4. تُظهر القياسات غالبًا إدراجات أسرع واستهلاك قرص أدنى. وجدت إحدى الدراسات مثلًا أن استخدام معرّفات مرتّبة زمنيًا (ULID أو v7) يسرّع التوليد ويخفض عبء الشبكة في الأنظمة الموزّعة؛ وفي اختباراتهم ضاعفت التحسينات سرعة توليد المعرّفات تقريبًا وقلّصت تخزين الفهارس وحركة البيانات بشكل ملموس. قد تختلف تجربتك، لكن الصورة العامة واضحة: قاعدة بياناتك «ستشكرك» على استخدام UUID مرتّب.
- **التوافقية:** يتبع UUIDv7 معيار UUID، فيصلح أينما طُلب «UUID». لدى PostgreSQL نوع UUID يخزن UUIDv7 دون أي تعديل (فهو قيمة 16 بايت). ويجري إضافة دعم v7 إلى مكتبات UUID في لغات كثيرة؛ فمنذ 2023–2024 توجد مكتبات في Java وGo وRust وPython وغيرها تولّد UUIDv7. وحتى إن كان الدعم الأصلي ناقصًا، يمكنك توليد UUIDv7 بأخذ خرج مكتبة ULID وصياغته كـ UUID. لا تحتاج لنوع عمود أو ترميز خاص لتخزينه — فهو يبقى UUID. تخزّنه في MySQL كـ BINARY(16)‎ أو عبر UNHEX()‎ على الصيغة النصية، وفي Postgres يمكنك التحويل من سلسلة إلى UUID أو استخدام دوال من الملحقات لتوليد v7، وثمة نقاش لإضافة uuid_generate_v7()‎ إلى ملحق pgcrypto أو ما شابه. وقد نُشرت مواصفة UUIDv7 رسميًا في RFC 9562 (مايو 2024) الذي حلّ محل RFC 4122 — لذا يتسارع وصول الدعم الرسمي عبر المنصات.
- **الفروق عن ULID:** يتشابه UUIDv7 والـ ULID كثيرًا، مع فروق دقيقة:
- **المعيارية:** UUIDv7 معيار رسمي (RFC 9562)، بينما الـ ULID ليس مقننًا رسميًا؛ إنه معيار واقعي رسّخه الاستخدام. يعني هذا أن لـ UUIDv7 مواصفة رسمية تلتزم بها كل التطبيقات.
- **التمثيل الكتابي:** يستخدم UUIDv7 صيغة UUID الست عشرية المألوفة بالشرطات، بينما يستخدم الـ ULID نص Base32 افتراضيًا. قد يؤثر الاختيار على سهولة الاستخدام: الـ ULIDs أقصر وأسهل قراءة للبشر، لكن UUIDv7 قد يندمج أفضل مع أدواتك إن كانت تتوقع سلاسل UUID ست عشرية. لا أفضلية تقنية لترميز على آخر سوى الطول وسهولة القراءة (سلسلة Base32 من 26 حرفًا مقابل ست عشرية من 36 حرفًا).
- **التخزين في قاعدة البيانات:** كلاهما 128 بت عند التخزين. نصيًا يستهلك الـ ULID 26 بايتًا وUUIDv7 يستهلك 36، لكن الأفضل تخزين كليهما ثنائيًا في 16 بايت. لا يمكنك وضع سلسلة ULID مباشرة في نوع UUID لدى PostgreSQL لأنها ليست بصيغة UUID (يمكن تحويل Base32 إلى ست عشري)، بينما يستخدم UUIDv7 نوع UUID فورًا. وفي MySQL ستستخدم BINARY(16)‎ للاثنين. لذا قد يكون UUIDv7 أسلس مع أعمدة UUID القائمة.
- **العشوائية:** لدى الـ ULID 80 بتًا عشوائيًا، ولدى UUIDv7 نحو 74 بتًا بحسب إعداد العدادات. عمليًا يمنحك كلاهما تفردًا وافرًا. الجزء العشوائي في ULID ثابت عند 80 بت، بينما يسمح UUIDv7 بتحويل بعض البتات إلى عداد إن احتجت توليد أعداد ضخمة في الميلي ثانية نفسها. فإذا استخدم تطبيقٌ ما عدادًا من 12 بت و62 بتًا عشوائيًا، فنظريًا قد يكون احتمال تصادم ULID الكامل (80 بت) أدنى — لكن 62 بتًا عشوائيًا أكثر من كافية كما أسلفنا. والتطبيقات الصحيحة لـ UUIDv7 تستخدم مولّد أرقام عشوائية آمنًا للجزء العشوائي فتبقى قوية. وجدت إحدى الدراسات أن خطر تصادم ULID أدنى بنسبة 98% من UUIDv7 في مخطط معيّن ببتات عشوائية أقل للإصدار 7 — لكن كليهما صفري عمليًا للأحجام الواقعية.
- **كشف المعلومات:** كلاهما يشفّر الطابع الزمني حتى الميلي ثانية، فكلاهما يكشف لحظة إنشاء المعرّف، وقد يفشي ذلك ساعة النظام التقريبية وترتيب الأحداث. إن احتجت معرّفات لا تكشف ترتيب إنشائها (مثلًا لمنع المستخدمين من استنتاج عدد السجلات بين معرّفين، أو لإخفاء توقيت إجراء ما)، فلن يصلح ULID ولا UUIDv7؛ عد إلى UUIDv4 أو مخطط عشوائي بالكامل. هذه نقطة تصميم مهمة للمعرّفات العامة: المفتاح الأساسي الداخلي قد لا يحتاج سرية، أما معرّف API خارجي فربما.

خلاصة: UUIDv7 لأغلب التطبيقات هو أفضل ما في العالمين — سرعة ULID وترتيبه، مع توافقية صيغة UUID وسهولتها. وكما قال أحد المصادر: «بأدوات اليوم، UUIDv7 هو الخيار الافتراضي للمفاتيح الأساسية الداخلية». لا تلجأ إلى ULID إلا حين تتطلب قابلية القراءة البشرية أو أسباب قديمة محددة ذلك، ولا إلى UUIDv4 إلا حين يتعذر كشف معلومات الوقت. والآن لنستعرض التوصيات جنبًا إلى جنب.

## حالات الاستخدام والتوصيات {#use-cases-and-recommendations}

**متى تستخدم UUIDv4:** إن أردت معرّفًا لا يحمل أي معلومات (كطوابع الوقت أو الترتيب) وتريده غير قابل للتنبؤ، يبقى UUIDv4 خيارًا وجيهًا. وهو متوفر في كل مكان بلا مكتبات إضافية أو عناية خاصة. استخدمه لرموز الأمان ومفاتيح الـ API والمعرّفات الخارجية حين لا تريد كشف وقت الإنشاء. إن كنت لا تريد أن يخمّن المستخدمون عدد طلبات نظامك من رقم الطلب، فـ UUIDv4 العشوائي مناسب. وفي حالات نادرة جدًا من التوليد فائق التوزع حيث قد يسبب حتى الطابع الزمني مشاكل، عشوائية v4 مفهومة وبسيطة. فقط اعلم أن ثمة ثمن أداء في قواعد البيانات؛ وللقواعد الداخلية الكبيرة، v4 ليس الخيار الأمثل عادة للأسباب المذكورة. وقد يخفف نمط «مفتاح داخلي تلقائي الزيادة + UUID كمرجع خارجي» بعض المشاكل لكنه يزيد التعقيد.

**متى تستخدم ULID:** الـ ULID خيار ممتاز للتطبيقات التي تحتاج معرّفات مرتّبة وسهلة القراءة للبشر. إن كنت تسجّل أحداثًا أو لديك نظام تظهر معرّفاته في السجلات أو الروابط، تجعل الـ ULIDs قراءتها أسهل. وهي مفيدة أيضًا في الواجهات أو الأماكن المرئية للمستخدمين، إذ يستطيع المرء تمييز الأحدث بين معرّفين (فهي تُفرز أبجديًا حسب الزمن). الـ ULIDs رائعة لأنظمة تخزين الأحداث (Event Sourcing) والسجلات والمراسلة وكل مكان تريد فيه حفظ ترتيب الحدوث وربما استنتاج وقت الحدث من المعرّف بسرعة. وتُستخدم لبناء مفاتيح قابلة للفرز تقريبًا (k-sortable) في كثير من قواعد NoSQL وأنظمة الرسائل، فيسرّع ذلك استعلامات النطاق الزمني. انتبه إلى أن الـ ULIDs تعتمد على الساعات، فاحرص على مزامنة ساعات أنظمتك إلى حد معقول؛ إن انحرفت ساعة جهاز كثيرًا فستعكس معرّفاته ذلك (ومع أن الطوابع خارج الترتيب ستستقر بالفرز حسب الوقت المطلق، قد ترى بعض الاختلال إذا تأخرت ساعة خادم قليلًا). وإذا كنت تتعامل مع المعرّفات يدويًا أحيانًا — كنسخ معرّف من مكان لآخر — تفيدك الـ ULIDs لخلوها من الشرطات وقصرها فتقل الأخطاء. وإن كانت بيئتك لا تدعم UUIDv7 بسهولة بعد، فالـ ULID بديل مجرّب — فقط استخدم مكتبة جيدة تتقن الزيادة الرتيبة، وفكّر في تخزينها ثنائيًا للكفاءة إن لم تكن بحاجة لقراءتها مباشرة في قاعدة البيانات.

**متى تستخدم UUIDv7:** الـ UUIDv7 افتراضي ممتاز لمعظم الأنظمة الجديدة، خصوصًا الـ microservices وقواعد البيانات الداخلية. فهو ملتزم بمعيار UUID ويمنحك فوائد ULID نفسها تقريبًا في الفرز والفهرسة. إن كنت في بيئة تتبنى المعايير الجديدة بسرعة أو تريد ضمان جاهزية معرّفاتك للمستقبل، فهو الخيار الأفضل. مثلًا، إن كنت على PostgreSQL وتريد نوع UUID الأصلي لسهولته، ولّد UUIDv7 في تطبيقك وخزّنه مباشرة بلا مشاكل. وهو ممتاز للمفاتيح الأساسية حيث تريد تفادي مشاكل الزيادة التلقائية — كالوصول لحدود التسلسل أو التنافس عليه — مع الاحتفاظ بسرعة الإدراج التسلسلي. ويفيد كذلك في الأنظمة الموزّعة حيث تساعد الطوابع الزمنية داخل المعرّفات على التنقيح وترتيب الأحداث عالميًا. نقطة أخيرة: إن كان الطابع الزمني المدمج يُعد حساسًا (قد يكشف سلوك المستخدمين في بعض الحالات)، فعامل المعرّفات نفسها كبيانات حساسة. أما للاستخدام الداخلي — مفاتيح قواعد البيانات والمراسلة الداخلية — فذلك ليس مشكلة عادة.
