วันก่อนที่ทีม HB กำลังสนทนากันอยู่ และ Ben หัวหน้าฝ่ายพัฒนาของเรากล่าวว่าเขาต้องการใช้ ULID แทน UUID สำหรับระบบเฉพาะ
เช่นเดียวกับวิศวกรผู้มากประสบการณ์ ปฏิกิริยาของฉันคือการพึมพำบางอย่างที่ไม่ผูกมัด จากนั้นแอบไปที่ Google เพื่อพยายามค้นหาว่า ULID คืออะไร
สองชั่วโมงต่อมา ฉันก็ปรากฏตัวขึ้นพร้อมกับจ้องเขม็งเป็นพันหลา และตระหนักว่าโลกของตัวระบุที่ไม่ซ้ำกันนั้นใหญ่กว่าและมหัศจรรย์กว่าที่ฉันจะจินตนาการได้
ก่อนที่เราจะเริ่มต้นกับ ULID ให้กลับไปที่พื้นฐานและหารือเกี่ยวกับ UUIDs:
รหัส "ปกติ" มีปัญหาอย่างไร
เว็บแอปพลิเคชันส่วนใหญ่ที่ใช้ฐานข้อมูลเริ่มต้นเป็นรหัสตัวเลขที่เพิ่มขึ้นโดยอัตโนมัติ ตัวอย่างเช่น ใน Rails คุณอาจเห็นพฤติกรรมเช่นนี้:
p1 = Person.create!
p1.id
# => 1
p2 = Person.create!
p2.id
# => 2
ฐานข้อมูลสามารถสร้างรหัสตามลำดับได้เพราะมันเก็บตัวนับที่เพิ่มในการสร้างบันทึก
รูปแบบนี้ยังสามารถเห็นได้นอกฐานข้อมูล บางครั้งเราจำเป็นต้องกำหนดรหัสด้วยตนเอง และเราอาจจัดเก็บตัวนับที่กำหนดเองใน - พูด - อินสแตนซ์ Redis
รหัสตามลำดับใช้งานได้ง่ายสำหรับกรณีการใช้งานที่มีปริมาณน้อย แต่จะกลายเป็นปัญหามากขึ้นเมื่อปริมาณเพิ่มขึ้น:
- เป็นไปไม่ได้ที่จะสร้างบันทึกพร้อมกันเพราะการแทรกแต่ละครั้งต้องรอในแถวเพื่อรับรหัส
- การขอรหัสตามลำดับอาจต้องมีการเดินทางไปกลับของเครือข่ายและส่งผลให้ประสิทธิภาพการทำงานช้าลง
- เป็นการยากที่จะขยายขนาดพื้นที่เก็บข้อมูลที่มีรหัสตามลำดับ คุณต้องกังวลว่าตัวนับบนเซิร์ฟเวอร์ต่าง ๆ จะไม่ซิงค์กัน
- เป็นเรื่องง่ายสำหรับโหนดที่มีตัวนับที่จะกลายเป็นจุดล้มเหลวเพียงจุดเดียว
รหัสตามลำดับยังรั่วข้อมูล ซึ่งอาจเป็นปัญหาในบางกรณี:
- คุณสามารถเดารหัสของทรัพยากรที่อาจไม่ใช่ของคุณได้อย่างง่ายดาย
- หากคุณสร้างผู้ใช้และรหัสคือ 20 คุณจะรู้ว่าบริการนี้มีผู้ใช้ 20 ราย
UUID เป็นขนาดเว็บ
UUID ดูแตกต่างจากรหัสลำดับเล็กน้อย เป็นตัวเลข 128 บิต โดยทั่วไปจะแสดงเป็นเลขฐานสิบหก 32 หลัก:
123e4567-e89b-12d3-a456-426655440000
UUID ถูกสร้างขึ้นโดยใช้อัลกอริธึมเฉพาะที่กำหนดไว้ใน RFC 4122 พวกเขาพยายามแก้ปัญหามากมายที่เกิดขึ้นกับรหัสตามลำดับ:
- คุณสามารถสร้าง UUID บนโหนดจำนวนเท่าใดก็ได้โดยไม่ต้องแชร์สถานะหรือการประสานงานระหว่างโหนด
- คาดเดาได้น้อยกว่ารหัสลำดับเล็กน้อย (เพิ่มเติมในภายหลัง)
- ไม่เปิดเผยขนาดของชุดข้อมูลของคุณ
สิ่งที่จับได้คือมีโอกาสเล็กน้อยที่โหนดสองโหนดจะสร้างรหัสเดียวกันโดยอิสระ เหตุการณ์นี้เรียกว่า "การชนกัน"
UUID หลากหลายรสชาติ
อัลกอริธึม UUID มีห้าประเภทที่กำหนดไว้ใน RFC 4122 ซึ่งแบ่งออกเป็นสองประเภท:
- ตามเวลาและการสุ่ม อัลกอริทึมคือสิ่งที่เรากำลังพูดถึง ส่งผลให้มี UUID ใหม่สำหรับทุกการวิ่ง
- ประเภทที่ 4 :รหัสที่สร้างขึ้นแบบสุ่ม น่าจะเป็นทางออกที่ดีที่สุดของเราสำหรับรหัสใหม่
- ประเภทที่ 1 :ID ประกอบด้วยที่อยู่ MAC ของโฮสต์และการประทับเวลาปัจจุบัน สิ่งเหล่านี้เลิกใช้แล้วเพราะเดาง่ายเกินไป
- ประเภทที่ 2 :สิ่งเหล่านี้ดูเหมือนจะไม่ธรรมดา ดูเหมือนว่าจะสร้างมาเพื่อ RPC แบบโบราณ
- อัลกอริธึมตามชื่อ แตกต่างกันเล็กน้อย พวกเขาสร้าง UUID เดียวกันสำหรับชุดอินพุตที่กำหนดเสมอ
- ประเภทที่ 5 :ใช้แฮช SHA-1 เพื่อสร้าง UUID แนะนำ
- ประเภทที่ 3 :ใช้แฮช MD5 และเลิกใช้แล้วเนื่องจาก MD5 ไม่ปลอดภัยเกินไป
ใน Ruby คุณสามารถสร้าง UUID ผ่าน uuidtools
อัญมณี. รองรับทุกประเภท ยกเว้นประเภท 2 ลึกลับ
# Code stolen from the uuidtools readme. :)
require "uuidtools"
# Type 1
UUIDTools::UUID.timestamp_create
# => #<UUID:0x2adfdc UUID:64a5189c-25b3-11da-a97b-00c04fd430c8>
# Type 4
UUIDTools::UUID.random_create
# => #<UUID:0x19013a UUID:984265dc-4200-4f02-ae70-fe4f48964159>
# Type 3
UUIDTools::UUID.md5_create(UUIDTools::UUID_DNS_NAMESPACE, "www.widgets.com")
# => #<UUID:0x287576 UUID:3d813cbb-47fb-32ba-91df-831e1593ac29>
# Type 5
UUIDTools::UUID.sha1_create(UUIDTools::UUID_DNS_NAMESPACE, "www.widgets.com")
# => #<UUID:0x2a0116 UUID:21f7f8de-8051-5b89-8680-0195ef798b6a>
ย้ายไปที่ ULID
หมายเหตุ: ในเวอร์ชันดั้งเดิมของบล็อกโพสต์นี้ ฉันลืมเชื่อมโยงไปยังข้อกำหนด ULID นี่มัน. มีลิงก์ไปยังการใช้งานใน Ruby และภาษาอื่นๆ
ULID เป็นแนวทางใหม่ที่มีประโยชน์สำหรับตัวระบุที่ไม่ซ้ำ ความแตกต่างที่ชัดเจนที่สุดคือมันดูแตกต่างออกไปเล็กน้อย:
01ARZ3NDEKTSV4RRFFQ69G5FAV
ประกอบด้วยตัวเลขที่เข้ารหัสฐาน 32 สองตัว การประทับเวลา UNIX ตามด้วยตัวเลขสุ่ม นี่คือโครงสร้างตามที่กำหนดไว้ในข้อกำหนด:
01AN4Z07BY 79KA1307SR9X4MV3
|----------| |----------------|
Timestamp Randomness
48bits 80bits
โครงสร้างนี้น่าทึ่งมาก! หากคุณจำได้ UUID จะขึ้นอยู่กับการประทับเวลาหรือการสุ่ม แต่ ULID ใช้การประทับเวลาทั้งสอง และ สุ่ม
ด้วยเหตุนี้ ULID จึงมีคุณสมบัติที่น่าสนใจบางประการ:
- สามารถจัดเรียงศัพท์ (เช่น เรียงตามตัวอักษร) ได้
- การประทับเวลามีความแม่นยำเป็นมิลลิวินาที
- สวยกว่า UUID :)
สิ่งเหล่านี้เปิดโอกาสที่ยอดเยี่ยม:
- หากคุณแบ่งพาร์ติชั่นฐานข้อมูลตามวันที่ คุณสามารถใช้การประทับเวลาที่ฝังใน ULID เพื่อเลือกพาร์ติชั่นที่ถูกต้องได้
- คุณสามารถจัดเรียงตาม ULID แทนคอลัมน์ created_at แยกต่างหากได้ หากยอมรับความแม่นยำระดับมิลลิวินาทีได้
มีข้อเสียที่เป็นไปได้เช่นกัน:
- หากการเปิดเผยการประทับเวลาเป็นแนวคิดที่ไม่ดีสำหรับแอปพลิเคชันของคุณ ULID อาจไม่ใช่ตัวเลือกที่ดีที่สุด
- The
sort by ulid
วิธีการอาจไม่ทำงานหากคุณต้องการความแม่นยำระดับต่ำกว่าเสี้ยววินาที - ตามอินเทอร์เน็ต การใช้งาน ULID บางอย่างไม่สามารถกันกระสุนได้
บทสรุป
UUID เป็นและจะเป็นมาตรฐานต่อไป พวกเขามีมาโดยตลอด และห้องสมุดก็มีให้ในทุกภาษาเท่าที่จะจินตนาการได้ อย่างไรก็ตาม แนวทางใหม่ๆ ก็คุ้มค่าที่จะพิจารณา โดยเฉพาะอย่างยิ่งเมื่อเราเข้าสู่โลกที่ดำเนินการโดยระบบแบบกระจายมากขึ้น แนวทางรหัสที่ไม่ซ้ำกันใหม่อาจช่วยเราแก้ปัญหาที่ไม่แพร่หลายในการเผยแพร่ RFC4122