โอกาสเกิดการชนกันโดยใช้บิตที่สำคัญที่สุดของ UUID ใน Java


235

หากฉันใช้ความLong uuid = UUID.randomUUID().getMostSignificantBits()เป็นไปได้ที่จะได้รับการชน มันตัดบิตที่สำคัญน้อยที่สุดออกไปดังนั้นจึงมีความเป็นไปได้ที่คุณจะชนกันใช่ไหม?

คำตอบ:


213

ตามเอกสารคู่มือเมธอดสแตติกUUID.randomUUID()สร้างชนิด UUID 4

ซึ่งหมายความว่ามีการใช้หกบิตสำหรับข้อมูลบางประเภทและ 122 บิตที่เหลือจะถูกกำหนดแบบสุ่ม

บิตที่ไม่ใช่แบบสุ่มจำนวนหกบิตถูกกระจายด้วยสี่ในครึ่งที่สำคัญที่สุดของ UUID และสองในครึ่งที่สำคัญน้อยที่สุด ดังนั้นครึ่งที่สำคัญที่สุดของ UUID ของคุณจะมีการสุ่ม 60 บิตซึ่งหมายความว่าคุณโดยเฉลี่ยต้องสร้าง 2 ^ 30 UUID เพื่อให้ได้รับการชนกัน (เทียบกับ 2 ^ 61 สำหรับ UUID เต็มรูปแบบ)

ดังนั้นฉันจะบอกว่าคุณค่อนข้างปลอดภัย อย่างไรก็ตามโปรดทราบว่าสิ่งนี้ไม่เป็นความจริงสำหรับ UUID ประเภทอื่นเช่นที่ Carl Seleborg กล่าวถึง

บังเอิญคุณจะดีขึ้นเล็กน้อยโดยใช้ครึ่งหนึ่งที่สำคัญน้อยที่สุดของ UUID (หรือเพียงแค่สร้างการสุ่มระยะยาวโดยใช้ SecureRandom)


3
ฉันไม่แน่ใจว่าสิ่งนี้ถูกต้องทั้งหมด - ดูการใช้งานเป็นที่ชัดเจนว่าข้อมูลรุ่น / ตัวแปรไม่ได้ถูกเก็บไว้ในบิตที่สำคัญที่สุด แต่จะอยู่ที่ตรงกลาง
Tom

2
@RasmusFaber ความคิดเห็นโดยTomถูกต้อง: คำตอบที่นี่ไม่ถูกต้องเกี่ยวกับหกบิตที่สำคัญที่สุดที่เป็นข้อมูลประเภท มีข้อมูลที่ไม่ใช่การสุ่มหกบิต แต่สี่บิตระบุว่าเวอร์ชั่น 4 และอีกสองบิตถูกสงวนไว้ บิตสี่และสองอยู่ในตำแหน่งต่าง ๆ ใกล้กับตรงกลางของค่า 128- บิต ดูบทความวิกิพีเดีย
Basil Bourque

56

เรย์มอนด์เฉินมีโพสต์บล็อกที่ยอดเยี่ยมจริงๆในนี้:

GUID นั้นไม่ซ้ำกันทั่วโลก แต่สตริงย่อยของ GUID ไม่ใช่


1
ลิงค์ไม่ตายอีกต่อไป
Dávid Veszelovszki

3
ลิงก์ตายอีกครั้ง นี่คือการเชื่อมโยงไปยังเว็บรุ่นเก็บ
Kuba Spatny


10

คุณจะดีกว่าเพียงแค่สร้างค่ายาวแบบสุ่มจากนั้นบิตทั้งหมดจะถูกสุ่ม ใน Java 6, Random ใหม่ () ใช้ System.nanoTime () บวกตัวนับเป็นเมล็ด

มีเอกลักษณ์ที่แตกต่างกันไป

หากคุณต้องการความเป็นเอกลักษณ์ในหลาย ๆ เครื่องคุณอาจมีตารางฐานข้อมูลส่วนกลางสำหรับการจัดสรรรหัสที่ไม่ซ้ำกันหรือแม้กระทั่งรหัสที่เป็นเอกลักษณ์

หากคุณต้องการมีเอกลักษณ์ในหนึ่งแอพคุณสามารถมีตัวนับ (หรือตัวนับที่เริ่มต้นจาก currentTimeMillis () * 1,000 หรือ nanoTime () ขึ้นอยู่กับความต้องการของคุณ)


7

ใช้เวลาYYYYDDDD(ปี + วันแห่งปี) เป็นคำนำหน้า สิ่งนี้จะลดการกระจายตัวของฐานข้อมูลในตารางและดัชนี byte[40]วิธีนี้ผลตอบแทน ฉันใช้มันในสภาพแวดล้อมไฮบริดที่ Active Directory SID ( varbinary(85)) เป็นกุญแจสำคัญสำหรับผู้ใช้ LDAP และใช้ ID ที่สร้างขึ้นอัตโนมัติของแอปพลิเคชันสำหรับผู้ใช้ที่ไม่ใช่ LDAP นอกจากนี้ธุรกรรมจำนวนมากต่อวันในตารางธุรกรรม (อุตสาหกรรมการธนาคาร) ไม่สามารถใช้Intประเภทมาตรฐานสำหรับคีย์ได้

private static final DecimalFormat timeFormat4 = new DecimalFormat("0000;0000");

public static byte[] getSidWithCalendar() {
    Calendar cal = Calendar.getInstance();
    String val = String.valueOf(cal.get(Calendar.YEAR));
    val += timeFormat4.format(cal.get(Calendar.DAY_OF_YEAR));
    val += UUID.randomUUID().toString().replaceAll("-", "");
    return val.getBytes();
}

3
ทำไมไม่ใช้ V1 UUID มาตรฐานแทน
ShadowChaser
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.