مولّد UUID

ولّد معرّفات UUID من الإصدار v4 أو v7 المرتّب زمنيًا، وافحص أي معرّف لديك.

5

معرّفات عشوائية

    افحص معرّفًا

    الصق واحدًا لترى إصداره وما بداخله إن كان بداخله شيء.

    الإصدارات، وأيّها يهم فعلاً

    هي ثمانية، وفي العمل الجديد ينحصر الاختيار عملياً بين اثنين. أما البقية فيستحق أن تعرفها حين تصادفها في نظام قديم.

    الإصدارمبني مناستعمله حين
    v4122 بتاً عشوائياًتحتاج ما لا يمكن تخمينه. وهو الخيار الافتراضي للرموز والمعرّفات.
    v7طابع زمني + عشوائيةتحتاج مفتاحاً لقاعدة بيانات. يُرتَّب بحسب وقت الإنشاء.
    v1طابع زمني + عنوان MACموروث. يفشي الجهاز والوقت.
    v3 / v5تجزئة لفضاء أسماء واسمتحتاج أن يعطي المدخل نفسه المعرّف نفسه دائماً.
    v6‏v1 بترتيب معادتنقل بيانات v1 وتريدها أن تُرتَّب.
    v8ما تقرره أنتلا يكاد يُستعمل أبداً.

    مشكلة الفهرس، وهي السبب الحقيقي لوجود v7

    هذه هي الحجة العملية، وتستحق أن تُفهَم لا أن تُؤخذ تسليماً.

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

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

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

    وبدقة: الترتيب مضمون بين المللي ثانية والأخرى لا داخل الواحدة منها. فمعرّفان من v7 وُلّدا في المللي ثانية نفسها يُرتَّبان بذيليهما العشوائيين، وهو ما يسمح به RFC 9562 ويتيح للتطبيقات اختيارياً تشديده بعدّاد. ولا فرق في ذلك على حجة تجاور الفهرس، فهي لا تعتمد إلا على الطابع الزمني في المقدمة.

    أما الكلفة الباقية على الاثنين فهي 16 بايتاً مقابل 4 أو 8، وتتكرر في كل فهرس يشير إلى المفتاح. وفي جدول كبير بعدة مفاتيح خارجية، فتلك مساحة قرص حقيقية.

    ماذا يفشي v7

    الطابع الزمني يقرؤه كل من يحمل المعرّف. الصق معرّف v7 في الفاحص أعلاه وسيخبرك بالمللي ثانية التي أُنشئ فيها.

    وهذا غير ضار عادةً. وأحياناً ليس كذلك: فمعرّف v7 في رابط عام يكشف متى أُنشئ السجل، وقد يكفي ذلك لاستنتاج معدلات التسجيل أو أحجام الطلبات أو أن مستنداً أُرِّخ بأثر رجعي. فإن كان ذلك يهم، فالتقسيم المباشر هو v4 لكل ما يراه المستخدم وv7 للمفاتيح الداخلية.

    حيث يجب أن تكون العشوائية حقيقية

    حساب التصادم الذي يستشهد به الجميع — 2.7 كوينتليون قبل بلوغ احتمال تصادم واحد حدّ رمي العملة — يفترض 122 بتاً عشوائياً حقاً. وهو قول عن الصيغة لا عن تنفيذك أنت.

    فالمعرّف المبني على Math.random() لا يملك لا العشوائية ولا عدم القابلية للتنبؤ اللتين يوحي بهما ذلك الرقم، والتصادمات في الواقع كانت دائماً تقريباً بسبب هذا لا بسبب سوء الحظ. فعمليتان بدأتا في الثانية نفسها بالبذرة نفسها تنتجان التسلسل نفسه.

    وتستعمل هذه الصفحة crypto.randomUUID() حيث توجد وcrypto.getRandomValues فيما عدا ذلك. ويستحق الأمر أن تتحقق مما تستعمله شيفرتك أنت، لا سيما إن كانت معرّفات UUID تقوم مقام الرموز المميزة — فمعرّف إعادة تعيين كلمة مرور قابل للتنبؤ يعني استيلاءً كاملاً على الحساب.

    تخزينها بكفاءة

    الصيغة النصية ذات الستة والثلاثين محرفاً مريحة ومبذّرة. وإليك البدائل، وكلها الـ 128 بتاً نفسها:

    • نوع UUID الأصلي — في Postgres واحد منه، ويخزّن 16 بايتاً. استعمله.
    • `BINARY(16)` — المكافئ في MySQL، ونحو نصف مساحة CHAR(36).
    • Base64 — 22 محرفاً، حين يجب أن يكون نصاً ويهم الطول.
    • Base32 — 26 محرفاً ولا يفرّق بين الحالتين، وهو ما تريده إن كان إنسان سيقرأ أحدها بصوت عالٍ يوماً.

    ذات صلة

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

    أسئلة حول UUID

    هل يمكن أن يتصادم معرّفان UUID؟

    نظرياً نعم، وعملياً لا، ما دامت العشوائية حقيقية. فمعرّف UUID من الإصدار الرابع يحوي 122 بتاً عشوائياً، وستحتاج إلى توليد نحو 2.7 كوينتليون منها قبل أن تبلغ احتمال 50٪ لتصادم واحد — أي نحو مليار في الثانية طوال 85 سنة. والعطب الواقعي لا يكون في الرياضيات قط، بل في مصدر عشوائية ضعيف. فالمعرّف المبني على Math.random() أو على مولّد رديء البذرة يمكن أن يتصادم ويتصادم فعلاً، ولهذا تستعمل هذه الصفحة مصدر المتصفح التشفيري.

    هل أستخدم UUID مفتاحاً رئيسياً في قاعدة البيانات؟

    يعتمد على الإصدار، والفرق كبير. فالإصدار الرابع عشوائي، لذا تهبط الإدخالات المتتالية في مواضع عشوائية من فهرس شجرة B — فتنقسم الصفحات ويتشظى الفهرس وينخفض معدل الكتابة انخفاضاً ملموساً في الجداول الكبيرة. أما الإصدار السابع فيبدأ بطابع زمني بالمللي ثانية، فتُلحَق الإدخالات في النهاية كما في العدد التزايدي التلقائي مع بقاء التفرّد العالمي. فإن أردت مفاتيح UUID فاستخدم الإصدار السابع. وثمة كلفة تشمل الاثنين: 16 بايتاً مقابل 4 أو 8 للعدد الصحيح، في كل فهرس يشير إليه.

    ما هو UUID v7 وهل صار استعماله آمناً؟

    هو مرتّب زمنياً: 48 بتاً من مللي ثواني يونكس يتبعها 74 بتاً عشوائياً، وقد قُنّن في RFC 9562 في مايو 2024. وهو معيار منشور مدعوم في إصدارات Postgres الحالية وفي معظم منظومات اللغات وفي عدد وافر من المكتبات. وهو خيار افتراضي معقول للعمل الجديد. والخاصية الوحيدة التي ينبغي الانتباه لها أنه يضمّن وقت الإنشاء، فمعرّف من الإصدار السابع داخل رابط يفشي تقريباً متى أُنشئ السجل.

    هل UUID آمن بما يكفي لاستعماله رمزاً مميزاً؟

    معرّف من الإصدار الرابع مأخوذ من مصدر تشفيري يحمل 122 بتاً من العشوائية، وهذا أكثر من كافٍ لرابط إعادة تعيين كلمة مرور أو لمعرّف جلسة. لكن هناك تحفظان. الأول أن المصدر يجب أن يكون تشفيرياً فعلاً — فـ crypto.randomUUID كذلك، ومعظم تطبيقات المكتبات كذلك، وكل ما بُني على Math.random() ليس كذلك. والثاني أن الإصدارات الأخرى لا تصلح: فالأول يضمّن طابعاً زمنياً وتاريخياً عنوان MAC، والثالث والخامس تجزئتان حتميّتان لمدخلاتهما، والسابع يفشي وقت إنشائه. فإن وجب ألا يكون قابلاً للتخمين فاستعمل الإصدار الرابع.

    لماذا يبلغ طول UUID ستةً وثلاثين محرفاً وهو 16 بايتاً فقط؟

    لأنه يُكتب بالنظام الست عشري — محرفان لكل بايت، فتصير 32 — زائد أربع شرطات. وبعض الأنظمة تخزّن البايتات الستة عشر الخام بدل ذلك، وهذا ما وُجد له BINARY(16) في MySQL، وهو يقلّص التخزين إلى النصف تقريباً. وأنظمة أخرى تستعمل ترميزاً نصياً أقصر: فـ Base64 يعطي 22 محرفاً، وBase32 يعطي 26 ولا يفرّق بين الحالتين. وكلها الـ 128 بتاً نفسها بثياب مختلفة.

    آخر مراجعة . وجدت شيئاً لم يعد محدَّثاً؟ أخبِرنا.