مولّد UUID v7

يولد UUID v7 مرتبة زمنياً لمفاتيح قواعد البيانات الأولية

تنسيق

العدد:

ما هو UUID v7؟

UUID v7 هو تنسيق UUID من الجيل التالي موحَّد في RFC 9562 (مايو 2024). يرمّز طابعاً زمنياً Unix بمللي ثانية 48 بت في البتات الأكثر أهمية، تليها علامات الإصدار والمتغيّر، ويملأ البتات المتبقية ببيانات عشوائية آمنة تشفيرياً.

لأن الطابع الزمني يشغل البتات العليا، تُرتَّب قيم UUID v7 زمنياً — لغوياً وعددياً على حد سواء. هذا يجعلها مناسبة تماماً للمفاتيح الأساسية في قواعد البيانات، حيث تتسبّب UUID العشوائية (v4) في تشرذم فهارس B-tree.

لماذا تشرذم UUID العشوائية فهارس قواعد البيانات

فهارس B-tree — المستخدمة في PostgreSQL وMySQL وSQLite ومعظم قواعد البيانات الأخرى — تبقي الصفوف مرتّبة حسب قيمة المفتاح. حين تدرج صفاً جديداً، يجب على قاعدة البيانات وضعه في الموضع المرتّب الصحيح داخل شجرة الفهرس.

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

تخطيط بتات UUID v7

UUID v7 عرضه 128 بت، مقسَّمة على ستة حقول:

البتاتالحقلالغرض
48unix_ts_msطابع زمني Unix بالمللي ثانية 48 بت — يشغل النصف الأعلى بالكامل
4verرقم الإصدار — دائماً 0111 (عشري 7)
12rand_a12 بت من بيانات عشوائية آمنة تشفيرياً
2varعلامة المتغيّر — دائماً 10 (متغيّر RFC 4122)
62rand_b62 بت من بيانات عشوائية آمنة تشفيرياً

دقة الطابع الزمني ملّي ثانية واحدة. داخل المللي ثانية ذاتها، تُرتَّب قيم UUID v7 بلاحقتها العشوائية — لا يُضمَن أن تكون رتيبة تصاعدياً دون المللي ثانية، لكنها k-sortable: المعرّفات المولَّدة بالقرب من بعضها زمنياً ستُرتَّب بالقرب من بعضها في الفهرس.

UUID v7 مقابل UUID v1

يضمّ كلٌّ من UUID v1 وUUID v7 طابعاً زمنياً، لكنهما يختلفان اختلافاً كبيراً في التصميم:

الميزةUUID v7UUID v1
الأصل / القاعدة الزمنيةحقبة Unix (1 يناير 1970)الحقبة الغريغورية (15 أكتوبر 1582)
الدقة الزمنيةملّي ثانية واحدة100 نانوثانية
قابل للترتيبنعم — k-sortable بالتصميملا — حقول الوقت مبعثرة في تخطيط UUID
الخصوصيةلا تسريب لمعلومات المضيفيضمّ عنوان MAC للمضيف المولِّد
أداء فهرس قاعدة البياناتممتاز — إدخالات تسلسلية، تشرذم ضئيلرديء — غير تسلسلي رغم الطابع الزمني
المعيارRFC 9562 (2024)RFC 4122 (2005)
دعم المتصفح الأصيلليس بعد (لا يوجد crypto.randomUUID v7)غير متاح أصيلياً

لأي مشروع جديد يحتاج UUID مرتّبة زمنياً، يُفضَّل UUID v7 على UUID v1. UUID v1 قديم ويسرّب معلومات المضيف.

UUID v7 مقابل ULID

ULID (المعرّف الفريد العالمي القابل للترتيب لغوياً) يحلّ مشكلة مماثلة لـ UUID v7. إليك المقارنة:

UUID v7
  • يتّبع معيار UUID RFC 9562 — متوافق مع جميع أدوات UUID
  • تنسيق سداسي عشري بواصلات — معترَف به عالمياً
  • دعم أصيل لعمود UUID في قواعد البيانات
  • 128 بت إجمالاً
ULID
  • ترميز Crockford Base32 — 26 حرفاً، أكثر إيجازاً قليلاً
  • غير حساس لحالة الأحرف ويتجنّب الأحرف الملتبسة (I وL وO وU)
  • أكثر قابلية للقراءة البشرية للوهلة الأولى
  • يتطلب مكتبة — لا دعم أصيلي للمنصة

إذا كنت بالفعل في بيئة UUID (عمود uuid في PostgreSQL وواجهات برمجية تُعيد UUID)، استخدم UUID v7. إذا كنت تبدأ من الصفر وتفضّل ترميزاً أكثر ملاءمة للإنسان، فـ ULID بديل معقول.

استخدام UUID v7 في قواعد البيانات

لا تولّد معظم قواعد البيانات UUID v7 أصيلياً بعد، لكن يمكن تخزينه في أعمدة UUID القياسية وتوليده في كود التطبيق أو عبر الإضافات:

PostgreSQL
PostgreSQL: خزّن في عمود uuid. تضيف إضافة pg-uuidv7 وظيفة uuid_generate_v7() على مستوى الخادم إذا احتجت معرّفات يولّدها DB.
MySQL / MariaDB
MySQL / MariaDB: خزّن في عمود BINARY(16) أو CHAR(36). ولِّد في كود التطبيق. MySQL 8.0+ لديه دعم UUID مرتَّب عبر UUID_TO_BIN(UUID(), 1) لـ v1، لكن v7 يتطلب التوليد على مستوى التطبيق.
SQLite
SQLite: خزّن كـ TEXT (36 حرفاً) أو BLOB (16 بايت). ولِّد في كود التطبيق. الترتيب اللغوي على TEXT يعمل بشكل صحيح لأن UUID v7 يستخدم بادئة طابع زمني بعرض ثابت.

أمثلة الكود

UUID v7 غير متاح بعد عبر crypto.randomUUID(). استخدم مكتبة مثل uuidv7 (npm) حتى يصل الدعم الأصيل:

JavaScript (browser / Node.js 20+)
function generateUuidV7() {
  const buf = new Uint8Array(16)
  crypto.getRandomValues(buf)

  const ms = BigInt(Date.now())

  // Embed 48-bit Unix ms timestamp
  buf[0] = Number((ms >> 40n) & 0xFFn)
  buf[1] = Number((ms >> 32n) & 0xFFn)
  buf[2] = Number((ms >> 24n) & 0xFFn)
  buf[3] = Number((ms >> 16n) & 0xFFn)
  buf[4] = Number((ms >> 8n)  & 0xFFn)
  buf[5] = Number(ms & 0xFFn)

  // Set version 7 (0111xxxx)
  buf[6] = (buf[6] & 0x0F) | 0x70

  // Set variant (10xxxxxx)
  buf[8] = (buf[8] & 0x3F) | 0x80

  const hex = [...buf].map(b => b.toString(16).padStart(2, '0')).join('')
  return `${hex.slice(0,8)}-${hex.slice(8,12)}-${hex.slice(12,16)}-${hex.slice(16,20)}-${hex.slice(20)}`
}

// Node.js 20+ built-in
// import { randomUUID } from 'node:crypto'  // v4 only — no v7 yet in stdlib
Python (uuid7 library)
# pip install uuid7
import uuid_extensions

uid = uuid_extensions.uuid7()
print(uid)                       # e.g. 018e2b3d-1a2b-7000-8000-abc123456789
print(uid.time)                  # Unix ms timestamp embedded in the UUID

# Or as a plain string
from uuid_extensions import uuid7str
print(uuid7str())
PostgreSQL — generate UUID v7
-- PostgreSQL 13+ extension-free implementation
CREATE OR REPLACE FUNCTION uuid_generate_v7()
RETURNS uuid
LANGUAGE sql
AS $$
  SELECT encode(
    set_bit(
      set_bit(
        overlay(
          uuid_send(gen_random_uuid())
          PLACING substring(int8send(floor(extract(epoch FROM clock_timestamp()) * 1000)::bigint) FROM 3)
          FROM 1 FOR 6
        ),
        52, 1
      ),
      53, 1
    ),
    'hex'
  )::uuid;
$$;

-- Usage as a default primary key
CREATE TABLE events (
  id uuid PRIMARY KEY DEFAULT uuid_generate_v7(),
  payload jsonb,
  created_at timestamptz DEFAULT now()
);
TypeScript — extract timestamp from UUID v7
function extractTimestamp(uuid: string): Date {
  const hex = uuid.replace(/-/g, '')
  const ms = parseInt(hex.slice(0, 12), 16)  // first 48 bits = ms timestamp
  return new Date(ms)
}

const uid = '018e2b3d-1a2b-7000-8000-abc123456789'
console.log(extractTimestamp(uid).toISOString())
// → "2024-03-15T10:22:05.259Z"

الأسئلة الشائعة

هل UUID v7 متوافق للخلف مع UUID v4؟
نعم. UUID v7 يستخدم صيغة السلك ذاتها البالغة 128 بت و32 رقماً سداسياً بواصلات كسائر إصدارات UUID. أي نظام يخزّن أو ينقل UUID سيقبل UUID v7 دون تغييرات. نيبل الإصدار (7) وبتات المتغيّر تحدّده كـ v7 للأدوات التي تفحص بنية UUID.
هل يكشف UUID v7 طابع التوليد الزمني؟
نعم — الـ 48 بت الأولى هي طابع زمني Unix بالمللي ثانية، لذا يستطيع أي شخص لديه UUID تحديد وقت توليده تقريباً (بدقة المللي ثانية). إذا كان الكشف عن وقت الإنشاء مصدر قلق لحالة استخدامك، استخدم UUID v4 بدلاً من ذلك.
هل يمكنني استخدام UUID v7 كمفتاح أساسي لقاعدة البيانات دون عمود created_at منفصل؟
نعم. لأن UUID v7 يضمّ طابعاً زمنياً بدقة المللي ثانية، يمكنك فكّ ترميز تلك القيمة للحصول على وقت الإنشاء التقريبي. مع ذلك، للوضوح وقابلية الفهرسة، لا تزال كثير من الفرق تحتفظ بعمود created_at صريح وتستخدم UUID v7 فقط لعمود المعرّف.
كم مقدار الإنتروبيا في UUID v7؟
UUID v7 لديه 74 بت من البيانات العشوائية (12 بت في rand_a + 62 بت في rand_b). هذا أقل قليلاً من 122 بت في UUID v4 لكنه لا يزال يوفّر فضاءً خالياً من التصادم ضخماً للاستخدام العملي. العشوائية المخفَّضة هي المقايضة مقابل اكتساب قابلية ترتيب الطابع الزمني.
هل يدعم UUID v7 أصيلياً في المتصفحات أو Node.js؟
ليس بعد حتى أوائل 2025. نُشر معيار RFC 9562 في مايو 2024، ودعم المنصات لا يزال يلحق به. استخدم حزمة uuidv7 npm حالياً. قد يصل الدعم الأصيل عبر crypto.randomUUID({ version: 7 }) أو واجهات برمجية مماثلة في إصدارات مستقبلية من المتصفحات وNode.js.