كل المقالات

الطابع الزمني في ⁨UUIDv7⁩ ليس كما تظن

قواعد البيانات · 4 دقيقة قراءة ·

يُقدَّم ⁨UUIDv7⁩ عادةً بثلاث وعود: معرّفات تتزايد دائمًا، وطابع زمني يخبرك متى أُنشئ الصف، ومعرّفات لا يمكن تخمينها. في مقال بتاريخ 19 أكتوبر 2026، يرى ⁨Robert Alexander⁩ أن خاصية واحدة فقط مضمونة فعلًا، وهي التفرّد، وأن المعيار نفسه يسمح بليّ الباقي. إن كنت تستخدم ⁨HasUuids⁩ في ⁨Laravel⁩ فأنت تستخدم ⁨UUIDv7⁩، فمن المفيد أن تعرف على أي هذه الوعود يتكئ كودك.

المعيار يسمح للطابع الزمني بالانحراف

ينص ⁨RFC 9562⁩ على أن التطبيقات يجوز لها تعديل الطابع الزمني الفعلي، ولا يضمن مدى قرب قيمة الساعة من الوقت الحقيقي، ويسمح بزيادتها متقدمةً على الوقت الفعلي. والتوثيق في ⁨Postgres⁩ للدالة ⁨uuid_extract_timestamp⁩ يقول الشيء نفسه: القيمة المستخرجة ليست بالضرورة لحظة توليد المعرّف.

السبب مفاضلة بين إبقاء المعرّفات مرتبة وإبقاء الوقت دقيقًا. حين ترجع الساعة إلى الوراء، فإن المولّد الذي يرفض العودة بالزمن مضطر إلى الاستمرار في طابع زمني صار الآن في المستقبل.

مرور الوقت الحقيقي الساعة ترجع طابع المعرّف متقدم على الساعة
الترتيب يُحفظ بتجميد الطابع الزمني. وما دام مجمّدًا فهو ليس وقت الإنشاء.

⁨Python⁩ يوضح إلى أي حد قد يصل ذلك

يستخدم مثال ⁨Alexander⁩ الدالة ⁨uuid.uuid7()⁩ في ⁨Python 3.14⁩. يُرجع الساعة ساعة كاملة بينما يولّد البرنامج معرّفات في حلقة. المتوقع أن يكون الفرق بين الطابع المضمّن والساعة ⁨3,600⁩ ثانية ثم يتقلص. لكن مع أكثر من معرّف واحد في كل جزء من الألف من الثانية يكبر بدلًا من ذلك: طبع تشغيله نحو ⁨3,695⁩، ثم ⁨3,795⁩ و⁨3,894⁩ و⁨3,994⁩ ثانية. ونقاش المشكلة لدى ⁨Python⁩ يعدّ هذا متسقًا مع المعيار ومع تطبيقات أخرى، ويقول الكاتب إنه يرى السلوك نفسه في غيرها.

ماذا يفعل مولّد ⁨Laravel⁩

قرأت الكود الموجود في مجلد ⁨vendor/⁩ لهذا الموقع بدل التخمين. ينشئ ⁨HasUuids⁩ المفاتيح بالدالة ⁨Str::uuid7()⁩ التي تستدعي ⁨ramsey/uuid⁩. والصنف ⁨UnixTimeGenerator⁩ فيها يأخذ الوقت بالأجزاء من الألف من الثانية، ويحتفظ بآخر وقت استخدمه في خاصية ساكنة. إن كان الوقت الجديد أحدث اختار بتات عشوائية جديدة. وإن لم يكن، سواء في الجزء نفسه من الثانية أو مع ساعة رجعت، أبقى آخر وقت وزاد الجزء العشوائي. وإن فاضت تلك الزيادة، قدّم الوقت المحفوظ جزءًا واحدًا من الألف من الثانية.

ومعنى ذلك عمليًا، من الكود وحده: داخل عملية ⁨PHP⁩ واحدة لا يرجع الطابع الزمني أبدًا، وبعد رجوع الساعة تحمل المعرّفات الجديدة الوقت الأقدم الذي يبدو أحدث حتى تلحق الساعة. وآخر وقت محفوظ في الذاكرة لا على القرص، فالعملية الجديدة تبدأ من الصفر، وخادمان لا يعرف أحدهما شيئًا عن ساعة الآخر. لا شيء فيه يَعِد بترتيب بين العمليات أو الأجهزة.

تحفّظان أصغر

الطابع الزمني يتسرّب. لا يمكنك مشاركة ⁨UUIDv7⁩ دون مشاركة وقت إنشائه بدقة جزء من الألف من الثانية. يعدّ الكاتب هذا أمرًا منخفض الأهمية لمعظم التطبيقات، لكن رابط ملف شخصي يكشف وقت تسجيل الحساب مثال تهمّ فيه المسألة.

والترتيب تقريبي. لا يمكن مزامنة ساعات أجهزة مختلفة بدقة جزء من الألف من الثانية، والتطبيق الذي لا يستخدم عدّادًا قد ينتج معرّفات خارج الترتيب داخل حلقة واحدة. يبقى الترتيب التقريبي مفيدًا لتجاور البيانات في الفهرس، لكنه ليس تسلسلًا.

الوقت (6 بايتات) الإصدار + جزء عشوائي يقرؤه كل من يملك المعرّف عمود ⁨created_at⁩ السجل الحقيقي
استخدم المعرّف للهوية، واحفظ الوقت في عمود تتحكم فيه.

ما الذي تعتمد عليه في تطبيق ⁨Laravel⁩

اعتمد على التفرّد، وعلى أن ترتيب الإدراج التقريبي مفيد للفهرس. لا تحلّل المعرّف لتعرف متى أُنشئ الصف: احتفظ بعمود ⁨created_at⁩ ورتّب به حين يهمّ الترتيب شخصًا ما. لا تستخدم المعرّف دليلًا على أي حدث وقع أولًا عبر الخوادم. وإن كان وقت الإنشاء خاصًا فلا تضع ⁨UUIDv7⁩ في رابط عام. وإن أردت أن ترى بنية معرّف منها، فإن مولّد ⁨UUID⁩ في هذا الموقع ينشئ معرّفات ⁨v7⁩ داخل متصفحك.

يضمن ⁨UUIDv7⁩ معرّفًا فريدًا. أما الطابع الزمني بداخله فتلميح يحق للمولّد أن يليّه، فاحتفظ بعمود ⁨created_at⁩ سجلًا لوقت الإنشاء.

الخطوة التالية: ابحث في كودك عن أي موضع يقرأ الوقت من معرّف، أو يرتّب بالمعرّف حيث يتوقع شخصٌ ترتيبًا زمنيًا، وانقله إلى ⁨created_at⁩. المصدر: مقال ⁨Robert Alexander⁩ عن خصائص ⁨UUIDv7⁩.

ملاحظات القراء

  1. لا توجد ملاحظات بعد. الأولى دائمًا الأجرأ.
باغي تبني شي حاجة بحال هادي؟ نقدر نعاونك. راسلني على واتساب