UUID แต่ละเวอร์ชันและช่วงเวลาที่ควรใช้งาน
(ntietz.com)- UUID มีทั้งหมด 8 เวอร์ชัน ตั้งแต่ v1 ถึง v8 และการที่ตัวเลขสูงกว่าไม่ได้หมายความว่าใหม่กว่าหรือดีกว่า แต่เป็นวิธีการสร้างที่แตกต่างกันซึ่งกำหนดไว้ใน RFC 9562
- ตัวเลือกที่ใช้กันทั่วไปจะเหลือหลัก ๆ คือ v4 และ v7 โดย v4 เป็นค่าเริ่มต้นสำหรับ ID แบบสุ่ม ส่วน v7 เหมาะเมื่อจำเป็นต้องเรียงลำดับตามเวลาที่สร้าง
- v1 และ v6 ใช้วัตถุดิบเดียวกัน แต่ v6 เป็นรูปแบบที่สลับลำดับฟิลด์เพื่อให้เมื่อจัดเรียงแล้วจะเป็น ลำดับตามเวลาที่สร้าง
- v3 และ v5 สร้าง UUID โดยแฮชข้อมูลนำเข้า โดย v3 ใช้ MD5 และ v5 ใช้ SHA-1 และสามารถใช้ DNS กับ URL เป็นค่าป้อนเข้าได้
- หากเป็นไปได้ แนวทางที่ใช้ได้จริงคือใช้ v7 แทน v1·v6, ใช้ v5 เมื่อต้องการ UUID ที่อิงจากข้อมูลนำเข้า และพิจารณา v8 เมื่อต้องการ UUID แบบกำหนดเองทั้งหมด
วิธีสร้าง UUID ตามแต่ละเวอร์ชัน
- UUID มีเวอร์ชันตั้งแต่ v1 ถึง v8 และทั้งหมดถูกกำหนดไว้ใน RFC 9562
-
UUID แบบอิงเวลา
- UUID Version 1 สร้างจาก timestamp, ตัวนับแบบ monotonic และ MAC address
- UUID Version 6 ใช้ข้อมูลเดียวกับ v1 แต่สลับลำดับเพื่อให้เมื่อจัดเรียงแล้วเป็นลำดับตามเวลาที่สร้าง
- UUID Version 7 สร้างจาก timestamp และข้อมูลแบบสุ่ม
-
UUID แบบสุ่ม·กำหนดเอง
- UUID Version 4 สร้างจาก ข้อมูลแบบสุ่ม ทั้งหมด และใกล้เคียงกับรูปแบบที่หลายคนนึกถึงเมื่อพูดถึง UUID
- UUID Version 8 เป็นรูปแบบที่ผู้ใช้กำหนดเองได้ทั้งหมด ยกเว้นฟิลด์ version/variant ที่ทุกเวอร์ชันต้องมี
-
UUID แบบอิงแฮชของข้อมูลนำเข้า
- UUID Version 3 สร้างจาก MD5 hash ของข้อมูลที่ผู้ใช้ให้มา
- ใน RFC มี DNS และ URL เป็นค่าป้อนเข้าที่เป็นไปได้
- UUID Version 5 สร้างจาก SHA-1 hash ของข้อมูลที่ผู้ใช้ให้มา
- เช่นเดียวกับ v3 สามารถใช้ DNS และ URL เป็นค่าป้อนเข้าได้
- UUID Version 3 สร้างจาก MD5 hash ของข้อมูลที่ผู้ใช้ให้มา
-
UUID ที่สงวนไว้
- UUID Version 2 ถูกสงวนไว้สำหรับ security ID และไม่มีรายละเอียดที่เป็นที่รู้จัก
เกณฑ์การเลือกใช้งานจริง
- ในกรณีส่วนใหญ่ ตัวเลือกคือ v4 หรือ v7
- ถ้าต้องการเพียง ID แบบสุ่ม v4 เหมาะเป็นตัวเลือกเริ่มต้น
- หากต้องสามารถจัดเรียง ID ได้ อาจพิจารณา v7
- ตัวอย่างเช่น เมื่อใช้ UUID เป็น database key v7 อาจเป็นตัวเลือกหนึ่ง
- v5 หรือ v8 ค่อนข้างเหมาะกับกรณีที่ต้องการใส่ข้อมูลของตัวเองไว้ใน UUID
- สำหรับกรณีประเภทนี้ โดยมากผู้ใช้มักรู้อยู่แล้วว่าตัวเองต้องการสถานการณ์แบบใด
- ตาม RFC นั้น v7 ปรับปรุงจาก v1 และ v6 ดังนั้นหากเป็นไปได้ควรใช้ v7 แทน v1·v6
- หากจำเป็นต้องใช้ v1 หรือ v6 จริง ๆ ก็สามารถใช้ v6 ได้
- v2 ถูกสงวนไว้สำหรับงานด้านความปลอดภัยที่ไม่ได้ระบุรายละเอียด
- v3 ถูกแทนที่ด้วย v5 ซึ่งใช้แฮชที่แข็งแรงกว่า และหากจำเป็นต้องใช้ v3 ก็มีแนวโน้มว่าผู้ใช้จะรู้อยู่แล้วว่าทำไมจึงต้องใช้
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
การบอกว่าไม่มีรายละเอียดของ UUID v2 นั้นเป็นเรื่องเมื่ออ่านแค่ RFC ที่ขึ้นชื่อว่าคลุมเครือมากเท่านั้น: https://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#t...
แม้แทบจะไม่เคยเจอ แต่ก็มี version 0 UUID อยู่ด้วย ควรพูดถึงเพราะมันเป็นที่มาของบิตที่สงวนไว้ ซึ่งทำให้ “เวอร์ชัน” อื่น ๆ ในภายหลังสามารถนิยามแบบเข้ากันได้ ผมรวบรวมการสำรวจที่เกี่ยวข้องไว้ในไลบรารี UUID ของผม: https://github.com/okeeblow/DistorteD/blob/NEW%E2%80%85SENSA...
เพราะมันเท่ดีเลยตัดสินใจรองรับ แต่ยังต้องตัดสินใจเรื่อง date rollover และวิธีจัดการ Apollo UID ที่เก่ากว่านั้น
ตอนนี้กำลังเริ่มทำ RFC เชิงให้ข้อมูลเกี่ยวกับประเภท UUID ในอดีตที่นิยามอยู่ในพื้นที่ Variant 0-7 เพื่อช่วยให้ผู้คนเข้าใจ หากอยากร่วมอภิปรายหรือตรวจถ้อยคำ ดูได้ที่ https://github.com/yocto/draft-yocto-uuid
รายละเอียดหาได้ภายใน 2 นาที กดลิงก์ในบทความไปยังส่วนที่นิยาม DCE ใน RFC 9562 แล้วตามลิงก์แรกในย่อหน้านั้นเข้าไปยังสเปก จากนั้นค้นหา “UUID” แล้วไปที่ภาคผนวก A ก็มีทุกอย่าง
ชื่ออาจชวนสับสนว่า “Universal Unique Identifier” แต่เนื้อหาที่ต้องการมีครบ ควรกดดูอย่างน้อยลิงก์ที่ตัวเองใส่ไว้ในบทความ
จากบริบทที่อธิบายว่าเวอร์ชันอื่น ๆ ถูกสร้างจากองค์ประกอบเฉพาะบางอย่าง ประโยคนั้นชัดเจนว่าหมายถึงอย่างหลัง แม้ประโยคจะคลุมเครืออยู่บ้าง แต่คงไม่ถึงขั้นชวนให้เข้าใจผิด
แต่ภาคผนวกก็อ่านสนุกเหมือนภาพ snapshot ของยุคนั้น
ถ้ามี มาตรฐาน UUID แบบสั้น อย่าง
73WakrfVbNJBaAmhQtEeDvหรือbK7nP9xMก็คงดีพูดให้เคร่งครัดก็คงไม่ใช่ UUID เพราะอาจซ้ำกันที่ไหนสักแห่งได้ แต่ผมอยากได้ชุดมาตรฐาน ID ที่สุ่มและสั้นพอจะจำได้
ผมคิดว่าเหตุผลที่ไม่มีมาตรฐานที่ได้รับความนิยมกว่านี้คือคุณต้องยอมเสียอะไรบางอย่าง 128 บิตมีความเสี่ยงชนกันต่ำสำหรับแทบทุกการใช้งาน แต่ยิ่งเล็กลงก็ยิ่งต้องพิจารณาสถานการณ์เฉพาะและผลกระทบจากการชนกัน ทำให้ทำเป็นมาตรฐานได้ยาก หากใช้อินโค้ดแบบอื่นอย่าง base64 หรือ base85 ก็จะสั้นลง แต่ต้องแลกกับการแยกตัวพิมพ์ใหญ่/เล็ก ความปลอดภัยสำหรับ URL และอื่น ๆ: https://github.com/ulid/spec
มันเป็นเพียงการแสดงผลอีกแบบของ UUID เดียวกันและแปลงกลับได้ สุดท้าย UUID ก็คือค่า 128 บิต จึงเป็นการเขียนแทนมากกว่าการแปลงจริง ๆ
แม้จะเสียข้อดีด้านการเรียงลำดับแบบ monotonic ที่มีใน UUID บางเวอร์ชันไป แต่ base58 ปลอดภัยสำหรับ URL และไม่มีอักขระพิเศษ ค่ายังคงเก็บเป็นไบนารีได้ เช่น ใน Postgres สามารถใช้
byteaแทนคอลัมน์ข้อความได้ดูการอภิปรายได้ที่นี่: https://github.com/uuid6/new-uuid-encoding-techniques-ietf-d...
timestamp ของ UUID v7 เป็นการเปลี่ยนแปลงครั้งใหญ่สำหรับ Databend เราใช้มันค้นหาไฟล์ metadata ใน AWS S3 อย่างรวดเร็วด้วย timestamp ทำให้งานอย่าง vacuum เร็วขึ้นมาก
PR: https://github.com/datafuselabs/databend/pull/16049
ข้อดีใหญ่ของ UUID ที่เรียงตามเวลาคือ locality ที่ดีขึ้น เวลาใส่รายการใหม่เข้า index โดยมากจะกลายเป็นการต่อท้าย ซึ่งอาจถูกกว่าการแทรกแบบสุ่ม อย่างไรก็ดี อาจทำให้เกิด contention มากขึ้น จึงน่าพิจารณาไฮบริดที่ใส่บิตสุ่มบางส่วนไว้หน้า timestamp เพื่อสร้าง “shard” ที่เรียงลำดับได้ การอ่านก็มักกระจุกอยู่ที่ข้อมูลล่าสุดด้วย ดังนั้นการที่ข้อมูลล่าสุดอยู่ในที่เดียวและขึ้น cache ได้ดีจึงมีประโยชน์
เข้าใจวัตถุประสงค์ของ uuid2 ได้ยาก ไม่รู้ด้วยซ้ำว่ายังมีประเภทแบบนี้อยู่อีก และเห็น uuid2 ครั้งแรกตอนขอให้ Xandr ลบข้อมูลส่วนบุคคลของฉัน: https://news.ycombinator.com/item?id=40913915
อ่าน Wikipedia แล้วก็ยังไม่ค่อยเข้าใจว่าทำไมถึงสร้างสิ่งที่เรียกว่า “ตัวระบุเอกลักษณ์แบบสากล” แล้วยังต้องมีหลายประเภท และทำไมบางประเภทถึงถูกทำมาให้ตามรอยไปถึง PC ต้นทางได้ สงสัยว่าการผสมส่วนหนึ่งของรหัส MAC เข้าไปทำให้ uuid2 ใกล้เคียงการสุ่มมากขึ้นหรือมีเหตุผลอื่นกันแน่ จากมุมมองความเป็นส่วนตัว ก็สงสัยด้วยว่าทำไมไม่ใช้แค่ตัวระบุยาว ๆ ที่มีอักขระให้เลือกจำนวนมากเพื่อทำให้โอกาสซ้ำแทบเป็นศูนย์ไปเลย
เครื่องสองเครื่องใด ๆ จะสร้าง UID/UUID เดียวกันจากอินพุตสองค่าเดียวกัน และฝ่ายที่ได้รับข้อความที่ถูกระบุแล้วสามารถย้อนตัวระบุกลับไปเป็นองค์ประกอบเดิมได้ เพราะถูกออกแบบเป็นป้ายกำกับของข้อความชั่วคราว สองมิติจึงเป็นเวลาและ ID ฮาร์ดแวร์ โดยแรกเริ่มใช้หมายเลขซีเรียลของ Apollo และต่อมาจึงใช้ที่อยู่ฮาร์ดแวร์ Ethernet เป็นต้น
ผมคิดว่าความสับสนส่วนใหญ่มาจากการที่ในการใช้งาน AEGIS ช่วงแรก วิศวกรของ Apollo เริ่มใช้ UID แบบ “canned” หรือก็คือแบบคงที่และเป็นที่รู้จักกันดีสำหรับการระบุในระบบไฟล์ เมื่อเวลาผ่านไป การใช้งาน UUID โดยทั่วไปได้ย้ายจากตัวระบุชั่วคราวที่ตั้งใจให้ซ้ำกันได้ ไปเป็นตัวระบุแบบ canned ที่ต้องหลีกเลี่ยงการซ้ำกันโดยสิ้นเชิง และสองมิติก็กลายเป็นการสุ่มกับการสุ่มอีกอย่าง
ประวัติซับซ้อนกว่านั้น Microsoft จ้างหนึ่งในคนสำคัญของ Apollo ไปทำ MSRPC สำหรับ Windows NT จึงเกิด GUID ขึ้นด้วย GUID มีการจัดวางฟิลด์ต่างจาก UUID และตรงข้ามกับที่เอกสารจำนวนมากบอก มันไม่ใช่ mixed-endian Microsoft ชอบใช้ canned GUID ไม่ใช่แค่สำหรับ GUID ระบุข้อความ RPC ชั่วคราว แต่รวมถึงแทบทุกอย่างที่ต้องมีตัวระบุที่เป็นที่รู้จักกันดี เช่น คลาส COM, media codec เป็นต้น ตัวอย่าง: https://gix.github.io/media-types/
ขอโทษที่ลิงก์คลังของผมสองครั้งในช่องคอมเมนต์เดียวกัน แต่ผมเริ่มสรุปประวัตินี้ไว้ใน README ของไลบรารี UUID ของผมแล้ว และควรกลับไปเขียนต่อ Apollo เริ่มในปี 1980 ส่วนร่าง RFC ของ Leach/Salz UUID เพิ่งออกมาในปี 1998 ดังนั้นยังมีเรื่องราวมหาศาลที่ไม่อยู่ในมาตรฐานสมัยใหม่: https://github.com/okeeblow/DistorteD/blob/NEW%E2%80%85SENSA...
UUID v4 เป็นแค่ตัวสร้างไบต์สุ่มที่ใส่ขีดกลางในตำแหน่งที่กำหนดเท่านั้น ไม่จำเป็นต้องใช้มันก็ได้ ถ้าสร้างไบต์สุ่มเองก็ประหยัดพื้นที่ได้
ขีดกลางที่ไม่จำเป็นและข้อมูลเวอร์ชันอะไรพวกนี้ก็จะลดลงด้วย
คล้ายกับที่ที่อยู่ IPv4 เป็นตัวเลข 32 บิต และ “สี่ชุดคั่นด้วยจุด” เป็นหนึ่งในรูปแบบการแทนค่า ถ้าคิดว่า UUID เป็นรูปแบบสตริง แปลว่าจับแนวคิดพื้นฐานที่สุดเกี่ยวกับ UUID ผิดแล้ว แม้แค่อยากได้ตัวระบุแบบสุ่ม ผมก็คิดว่าการมีบิตแฟล็กเล็ก ๆ ใน UUID แบบสุ่มที่บอกว่า “อันนี้ตั้งใจให้เป็นแบบสุ่ม” เป็นเรื่องดี มันมีประโยชน์เมื่อเจอตัวระบุเดี่ยว ๆ ที่ไม่มีบริบท
จะถกเถียงได้ว่าการสร้าง namespace แยกกันระหว่างวิธีสร้างหลายแบบมีประโยชน์หรือไม่ แต่ตัวสร้างเลขสุ่มทั่วไปมีโอกาสสร้าง UUIDv4 ที่ถูกต้องเพียง 1/16 เท่านั้น แน่นอนว่าถ้าอยากสร้างตัวสร้าง UUID เอง การตั้งบิตให้ถูกต้องเป็นเรื่องเล็กน้อย
ถ้าเป็น Go ก็แค่
uuid.New().String()แต่ถ้าจะอ่านข้อมูลสุ่มด้วยcrypto/randแล้วแปลงเป็น base64 หรือ hex ต้องใช้บรรทัดและความพยายามมากกว่าแนะนำว่าอย่าใช้ เวอร์ชันที่อิง MAC ในทางทฤษฎีอาจรวมทุกแบบยกเว้น v4 และ v7 แต่ v1 แย่ที่สุด
v3 ก็มีปัญหาว่า MD5 ถูกเจาะอย่างรุนแรงแล้ว
ไม่รู้รายละเอียดนอกจากเวอร์ชัน 4 แต่สิ่งที่ดูเหมือนจะขาดไปและน่าจะมีประโยชน์จริง ๆ คือวิธีที่ใช้ ข้อมูล SHA256 กับ counter คล้ายกับ PBKDF2
มันอาจเป็นตัวระบุที่สืบทอดมาโดยรักษาความเป็นส่วนตัว และยังอาจพิสูจน์ได้แบบหลวม ๆ ว่า UUID บางตัวสืบทอดมาจาก seed ใด
นอกเหนือจากนั้น คุณก็คงอยากได้เอาต์พุตที่ยาวกว่านี้
ใช้ v7 ไปเลยก็พอ
ทีนี้ถึงตาผู้เชี่ยวชาญด้านความปลอดภัยบอกว่าไม่ใช่แล้ว
สำหรับการใช้งานจำนวนมาก ถ้าประมวลผลข้อมูลซ้ำแล้วสร้าง ID เดิมได้จะมีประโยชน์มาก แต่ผมไม่รู้วิธีมาตรฐานในการทำสิ่งนี้