كيف يتم إنشاء UUIDs



ربما تكون قد استخدمت بالفعل UUIDs في مشاريعك واعتقدت أنها فريدة من نوعها. دعونا نلقي نظرة على الجوانب الرئيسية للتنفيذ ونرى لماذا تعتبر UUIDs فريدة من نوعها عمليًا ، نظرًا لوجود احتمال ضئيل بحدوث نفس القيم.



يمكن إرجاع التنفيذ الحديث لـ UUIDs إلى RFC 4122 ، والذي يصف خمسة طرق مختلفة لإنشاء هذه المعرفات. سنستعرض كل منها ونستعرض تنفيذ الإصدار 1 والإصدار 4.



نظرية



UUID (معرف فريد عالميًا) هو رقم 128 بت يُستخدم في تطوير البرامج كمعرّف فريد للعناصر. تمثيلها النصي الكلاسيكي عبارة عن سلسلة من 32 حرفًا سداسيًا عشريًا ، مفصولة بواصلات إلى خمس مجموعات في نمط 8-4-4-4-12.



على سبيل المثال:



3422b448-2460-4fd2-9183-8000de6f8343


تم تضمين معلومات تنفيذ UUID في هذا التسلسل العشوائي للأحرف:



xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx


تحدد القيم في الموضعين M و N إصدار ومتغير UUID ، على التوالي.



الإصدار



يتم تحديد رقم الإصدار من خلال البتات الأربعة الأكثر أهمية في الموضع M. توجد اليوم الإصدارات التالية:





اختيار



يحدد هذا الحقل قالب المعلومات المضمنة في UUID. يعتمد تفسير كل وحدات البت الأخرى في UUID على قيمة المتغير.



نحدده من خلال أول 1-3 بتات الأكثر أهمية في الموضع N.





اليوم، الخيار 1 غالبا ما يتم استخدام، والذي MSB0يساوي 1و MSB1متساوين 0. يعني ذلك أنه نظرا البدل - بت المختارة - فقط القيم الممكنة 8، 9، Aأو B.



مذكرة:



1 0 0 0 = 8

1 0 0 1 = 9

1 0 1 0 = A

1 0 1 1 = B


لذلك إذا رأيت UUID مع هذه القيم في الموضع N ، فهذا هو المعرف في الخيار 1.



الإصدار 1 (الوقت + معرف مضيف فريد أو عشوائي)



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



يتم الحصول على المعرف من خلال تسلسل عنوان MAC 48 بت ، وطابع زمني 60 بت ، وتسلسل ساعة "فريد" 14 بت ، و 6 بتات محجوزة للإصدار والمتغير UUIDs.



تسلسل الساعة هو ببساطة قيمة تزداد في كل مرة يتم فيها تغيير الساعة.


الطابع الزمني المستخدم في هذا الإصدار هو عدد الفواصل الزمنية التي تبلغ 100 نانوثانية منذ 15 أكتوبر 1582 ، تاريخ نشأة التقويم الغريغوري.



قد تكون على دراية بنظام يونكس الزمني منذ بداية العصر. إنه مجرد نوع مختلف من اليوم صفر. هناك خدمات على الويب يمكنها مساعدتك في تحويل تمثيل زمني إلى تمثيل آخر ، لذلك دعونا لا نتطرق إلى هذا الموضوع.


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



يتم إنشاء الإصدار 1 من UUID على النحو التالي:



  1. يتم أخذ أقل 32 بت أهمية من الطابع الزمني الحالي لـ UTC. سيكون هذا أول 4 بايت (8 أحرف سداسية عشرية) من UUID [ TimeLow].
  2. يتم أخذ 16 بت الأوسط للطابع الزمني الحالي لـ UTC. سيكون هذا هو 2 بايت التالية (4 أحرف سداسية عشرية) [ TimeMid].
  3. يدمج البايتان التاليان (4 أحرف سداسية عشرية) 4 بتات من إصدار UUID مع 12 MSBs المتبقية من الطابع الزمني UTC الحالي (الذي يحتوي على إجمالي 60 بت) [ TimeHighAndVersion].
  4. تحدد البتات 1-3 التالية متغير إصدار UUID. تحتوي البتات المتبقية على تسلسل ساعة يضيف القليل من العشوائية لهذا التنفيذ. هذا يتجنب الاصطدامات عندما تعمل عدة مولدات UUID على نفس النظام: إما أن يتم ضبط ساعة النظام للمولد ، أو إبطاء تغيير الوقت [ ClockSequenceHiAndRes && ClockSequenceLow].
  5. آخر 6 بايت (12 حرفًا سداسيًا عشريًا ، 48 بت) هي "معرف العقدة" ، والذي يكون عادةً عنوان MAC للمولد [ NodeID].


يتم إنشاء UUID للإصدار 1 باستخدام التسلسل:



TimeLow + TimeMid + TimeHighAndVersion + (ClockSequenceHiAndRes && ClockSequenceLow) + NodeID 


نظرًا لأن هذا التنفيذ يعتمد على الساعة ، فنحن بحاجة إلى التعامل مع المواقف المتطورة. أولاً ، لتقليل الارتباط بين الأنظمة ، يتم بشكل افتراضي أخذ تسلسل الساعة كرقم عشوائي - يتم ذلك مرة واحدة فقط في دورة حياة النظام بالكامل. يمنحنا هذا فائدة إضافية تتمثل في دعم معرفات العقدة التي يمكن حملها عبر الأنظمة ، نظرًا لأن تسلسل الساعة الأولي مستقل تمامًا عن معرف العقدة.



تذكر أن الغرض الرئيسي من استخدام تسلسل الساعة هو إضافة بعض العشوائية إلى معادلتنا. تساعد بتات تسلسل الساعة على تمديد الطابع الزمني واستيعاب المواقف التي يتم فيها إنشاء UUIDs متعددة حتى قبل تغيير ساعة المعالج. بهذه الطريقة نتجنب إنشاء نفس المعرفات عندما يتم ضبط الساعة (الجهاز مغلق) أو يتغير معرف العقدة. إذا تم إرجاع الساعة إلى الوراء ، أو كان من الممكن إعادتها (على سبيل المثال ، أثناء إيقاف تشغيل النظام) ، ولا يمكن لمولد UUID التحقق من إنشاء المعرفات بطوابع زمنية لاحقة عن قيمة الساعة المحددة ، فيجب تغيير تسلسل الساعة. إذا عرفنا قيمتها السابقة ، يمكننا ببساطة زيادتها ؛خلاف ذلك ، يجب ضبطه بشكل عشوائي أو بجودة عالية PRNG.



الإصدار 2 (أمان بيئة الحوسبة الموزعة)



يتمثل الاختلاف الرئيسي في هذا الإصدار عن الإصدار السابق في أنه بدلاً من "العشوائية" في شكل وحدات البت الأقل أهمية في تسلسل الساعة ، يتم هنا استخدام خاصية المعرف المميزة للنظام. غالبًا ما يكون هذا مجرد معرف المستخدم الحالي. يتم استخدام الإصدار 2 بشكل أقل ، وهو يختلف قليلاً عن الإصدار 1 ، لذلك دعنا ننتقل.



الإصدار 3 (الاسم + تجزئة MD5)



إذا كانت هناك حاجة إلى معرفات فريدة لتسمية المعلومات أو تسميتها ، فعادة ما يتم استخدام الإصدار 3 أو الإصدار 5. من UUID ،



حيث تقوم بترميز أي كيانات "مسماة" (مواقع ، DNS ، نص عادي ، إلخ) في قيمة UUID. الأهم من ذلك ، سيتم إنشاء نفس UUID لنفس مساحة الاسم أو النص.



لاحظ أن مساحة الاسم نفسها هي UUID.



let namespace = “digitalbunker.dev”
let namespaceUUID = UUID3(.DNS, namespace)

// Ex: 
UUID3(namespaceUUID, “/category/things-you-should-know-1/”) 
4896c91b-9e61-3129-87b6-8aa299028058

UUID3(namespaceUUID, “/category/things-you-should-know-2/”) 
29be0ee3-fe77-331e-a1bf-9494ec18c0ba

UUID3(namespaceUUID, “/category/things-you-should-know-3/”) 
33b06619-1ee7-3db5-827d-0dc85df1f759


في هذا التطبيق ، يتم تحويل مساحة اسم UUID إلى سلسلة من البايتات متسلسلة مع اسم الإدخال ، ثم تجزئة مع MD5 ، مما ينتج عنه 128 بت لـ UUID. ثم نعيد كتابة بعض البتات لإعادة إنتاج معلومات الإصدار والإصدار بدقة ، ونترك الباقي كما هو.



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



باستخدام نفس الإدخال ، ستكون النسخة 3 و 5 UUIDs التي تم إنشاؤها محددة.



الإصدار 4 (PRNG)



أبسط تنفيذ.



6 بتات محجوزة للإصدار والمتغير ، ولا يزال هناك 122 بتًا متبقية. يولد هذا الإصدار ببساطة 128 بتًا عشوائيًا ثم يستبدل 6 منها ببيانات الإصدار والإصدار.



هذه UUIDs تعتمد بشكل كامل على جودة PRNG (مولد الأرقام العشوائية الزائفة). إذا كانت الخوارزمية بسيطة للغاية ، أو تفتقر إلى القيم الأولية ، فإن احتمالية تكرار المعرفات تزداد.



في اللغات الحديثة ، غالبًا ما يتم استخدام الإصدار 4 من UUID.



تنفيذه بسيط للغاية:



  1. نقوم بتوليد 128 بت عشوائي.
  2. أعد كتابة بعض البتات بالإصدار الصحيح ومعلومات الإصدار:



    1. خذ الجزء السابع و 0x0FAND لمسح النبل العالي. ثم 0x40يتم استخدام OR لتعيين الإصدار 4.
    2. ثم نأخذ البايت التاسع ونجري عملية 0x3FAND على c وعملية 0x80OR عليه.
  3. حول 128 بت إلى سداسي عشري وأدخل واصلات.


الإصدار 5 (الاسم + SHA-1-hash)



الاختلاف الوحيد عن الإصدار 3 هو أننا نستخدم خوارزمية التجزئة SHA-1 بدلاً من MD5. يُفضل هذا الإصدار على الإصدار الثالث (SHA-1> MD5).



ممارسة



من أهم مزايا UUIDs أن تفردها لا يعتمد على سلطة تفويض مركزية أو على التنسيق بين الأنظمة المختلفة. يمكن لأي شخص إنشاء UUID مع بعض الثقة في أنه لن يقوم أي شخص آخر بإنشاء هذه القيمة في المستقبل المنظور.



هذا يجعل من الممكن الجمع بين المعرفات التي أنشأها مختلف المشاركين في قاعدة بيانات واحدة ، أو نقل المعرفات بين قواعد البيانات مع احتمال ضئيل للتصادم.



يمكن استخدام UUIDs كمفاتيح أساسية في قواعد البيانات ، كأسماء فريدة للملفات التي تم تحميلها ، كأسماء فريدة لأي مصادر ويب. لا تحتاج إلى سلطة تفويض مركزية لتكوينها. لكن هذا حل ذو حدين. بسبب عدم وجود وحدة تحكم ، من المستحيل تتبع UUIDs التي تم إنشاؤها.



هناك بعض العيوب التي يجب معالجتها. تعمل العشوائية المتأصلة على زيادة الأمان ، ولكنها تجعل تصحيح الأخطاء أكثر صعوبة. أيضًا ، يمكن أن يكون UUID زائداً عن الحاجة في بعض المواقف. لنفترض أنه ليس من المنطقي استخدام 128 بت لتحديد البيانات التي يقل حجمها الإجمالي عن 128 بت بشكل فريد.



التفرد



قد يبدو أنه إذا كان لديك ما يكفي من الوقت ، يمكنك تكرار قيمة. خاصة في حالة الإصدار 4. لكن في الواقع ليس كذلك. إذا كنت ستولد مليار UUIDs في الثانية على مدى 100 عام ، فإن فرصة تكرار إحدى القيم ستكون حوالي 50٪. هذا في ضوء حقيقة أن PRNG يوفر قدرًا كافيًا من الانتروبيا (العشوائية الحقيقية) ، وإلا فإن احتمال حدوث مضاعفة سيكون أعلى. مثال توضيحي أكثر: إذا أنشأت 10 تريليون UUID ، فإن احتمال ظهور قيمتين متطابقتين هو 0.00000006٪.



وفي حالة الإصدار 1 ، ستتم إعادة ضبط الساعة على الصفر فقط في 3603. لذلك إذا كنت لا تخطط لاستمرار تشغيل خدمتك حتى عام 1583 ، فأنت في أمان.



ومع ذلك ، يبقى احتمال ظهور مزدوج ، وفي بعض الأنظمة يحاولون أخذ ذلك في الاعتبار. ولكن في الغالبية العظمى من الحالات ، يمكن اعتبار UUIDs فريدًا تمامًا. إذا كنت بحاجة إلى مزيد من الأدلة ، فإليك تصورًا بسيطًا لاحتمال الاصطدام في الممارسة العملية.



All Articles