ฉันรู้ว่าUUIDsแบบสุ่มมีความน่าจะเป็นที่ต่ำมากสำหรับการชนในทางทฤษฎี แต่ฉันสงสัยว่าในทางปฏิบัติแล้ว Java ของดีแค่ไหนrandomUUID()
ในแง่ของการไม่ชนกัน? ใครบ้างมีประสบการณ์แบ่งปัน
ฉันรู้ว่าUUIDsแบบสุ่มมีความน่าจะเป็นที่ต่ำมากสำหรับการชนในทางทฤษฎี แต่ฉันสงสัยว่าในทางปฏิบัติแล้ว Java ของดีแค่ไหนrandomUUID()
ในแง่ของการไม่ชนกัน? ใครบ้างมีประสบการณ์แบ่งปัน
คำตอบ:
UUID ใช้java.security.SecureRandom
ซึ่งควรจะเป็น "strong cryptographically" ในขณะที่การใช้งานจริงไม่ได้ระบุไว้และสามารถแตกต่างกันระหว่าง JVMs (หมายถึงคำสั่งที่เป็นรูปธรรมใด ๆ ที่ทำถูกต้องเพียงหนึ่ง JVM ที่เฉพาะเจาะจง) มันจะอาณัติว่าเอาท์พุทจะต้องผ่านการทดสอบเครื่องกำเนิดเลขสุ่ม
เป็นไปได้เสมอที่การใช้งานจะมีข้อผิดพลาดเล็กน้อยที่ทำลายสิ่งนี้ทั้งหมด (ดู OpenSSH key generation bug) แต่ฉันไม่คิดว่ามีเหตุผลที่เป็นรูปธรรมที่ต้องกังวลเกี่ยวกับการสุ่มของ Java UUIDs
Wikipedia มีคำตอบที่ดีมาก http://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions
จำนวนสุ่มรุ่น 4 UUID ที่จำเป็นต้องสร้างขึ้นเพื่อให้มีความน่าจะเป็น 50% ของการชนอย่างน้อยหนึ่งครั้งคือ 2.71 ล้านล้านคำนวณดังนี้:
...
จำนวนนี้เทียบเท่ากับการสร้าง 1 พันล้าน UUID ต่อวินาทีเป็นเวลาประมาณ 85 ปีและไฟล์ที่มี UUIDs นี้จำนวน 16 ไบต์ต่อ UUID จะอยู่ที่ประมาณ 45 Exabytes ซึ่งใหญ่กว่าฐานข้อมูลที่ใหญ่ที่สุดที่มีอยู่ในปัจจุบันหลายเท่า คำสั่งของเพตาไบต์นับร้อย
...
ดังนั้นเพื่อให้มีโอกาสในการทำซ้ำหนึ่งพันล้านครั้งต้องสร้าง UUIDs 103 ล้านล้านเวอร์ชัน 4
UUID.randomUUID()
ไม่ใช่เกี่ยวกับโอกาสทางทฤษฎีสำหรับตัวสร้างตัวเลขสุ่มที่สมบูรณ์แบบ
ใครบ้างมีประสบการณ์แบ่งปัน
มี2^122
ค่าที่เป็นไปได้สำหรับ UUID ประเภท 4 (ข้อมูลจำเพาะบอกว่าคุณสูญเสีย 2 บิตสำหรับประเภทและอีก 4 บิตสำหรับหมายเลขรุ่น)
สมมติว่าคุณต้องสร้าง UUID แบบสุ่ม 1 ล้านครั้งต่อวินาทีโอกาสที่จะเกิดการซ้ำซ้อนในช่วงชีวิตของคุณจะน้อยนิด และเพื่อตรวจจับสิ่งที่ซ้ำกันคุณจะต้องแก้ปัญหาในการเปรียบเทียบ 1 ล้าน UUID ใหม่ต่อวินาทีกับUUID ทั้งหมดที่คุณสร้างขึ้นก่อนหน้านี้1 !
โอกาสที่ทุกคนมีประสบการณ์ (เช่นสังเกตเห็นจริง ) ซ้ำกันในชีวิตจริงมีขนาดเล็กกว่าหายไปเล็กน้อย ... เนื่องจากความยากลำบากในทางปฏิบัติของการมองหาการชนกัน
แน่นอนว่าโดยทั่วไปคุณจะใช้ตัวสร้างตัวเลขแบบหลอกเทียมไม่ใช่แหล่งที่มาของตัวเลขสุ่มอย่างแท้จริง แต่ฉันคิดว่าเราสามารถมั่นใจได้ว่าหากคุณใช้ผู้ให้บริการที่เชื่อถือได้สำหรับตัวเลขสุ่มความแข็งแรงของการเข้ารหัสของคุณแล้วมันจะเป็นความแข็งแกร่งของการเข้ารหัสและความน่าจะเป็นของการทำซ้ำจะเหมือนกันสำหรับเครื่องกำเนิดตัวเลขสุ่มแบบอุดมคติ .
อย่างไรก็ตามหากคุณต้องใช้ JVM ที่มีตัวสร้างตัวเลขที่เข้ารหัส "แตก" การเดิมพันทั้งหมดจะปิด (และอาจรวมถึงการแก้ไขปัญหาบางอย่างสำหรับปัญหา "การขาดแคลนเอนโทรปี" ในบางระบบหรือความเป็นไปได้ที่มีคนแก้ไข JRE ของคุณทั้งในระบบของคุณหรือต้นน้ำ)
1 - สมมติว่าคุณใช้ "ไบนารี btree" ตามที่เสนอโดยผู้วิจารณ์ที่ไม่ระบุชื่อแต่ละ UUID จะต้องใช้O(NlogN)
บิตหน่วยความจำ RAM เพื่อแสดงN
UUIDs ที่แตกต่างกันโดยสมมติว่าความหนาแน่นต่ำและการกระจายแบบสุ่มของบิต ทีนี้คูณด้วย 1,000,000 และจำนวนวินาทีที่คุณจะทำการทดสอบ ฉันไม่คิดว่ามันใช้งานได้จริงในช่วงระยะเวลาที่จำเป็นในการทดสอบการชนของ RNG ที่มีคุณภาพสูง ไม่แม้แต่จะเป็นตัวแทนที่ฉลาด (สมมุติ)
ฉันไม่ใช่ผู้เชี่ยวชาญ แต่ฉันคิดว่ามีคนฉลาดพอที่จะดูตัวสร้างตัวเลขสุ่มของ Java ในช่วงหลายปีที่ผ่านมา ดังนั้นฉันจะสมมติว่า UUID แบบสุ่มนั้นดี ดังนั้นคุณควรมีความน่าจะเป็นในการชนเชิงทฤษฎี (ประมาณ 1: 3 × 10 ^ 38สำหรับ UUID ที่เป็นไปได้ทุกคนทราบหรือไม่ว่าการเปลี่ยนแปลงนี้สำหรับ UUID แบบสุ่มเท่านั้นเป็น1/(16*4)
อย่างไร
จากประสบการณ์จริงของฉันฉันไม่เคยเห็นการชนใด ๆ เลย ฉันอาจจะเติบโตเครายาวอย่างน่าประหลาดใจในวันที่ฉันได้รับครั้งแรกของฉัน;)
ที่อดีตนายจ้างเรามีคอลัมน์เฉพาะที่มี uuid แบบสุ่ม เราได้รับการปะทะกันในสัปดาห์แรกหลังจากนำไปใช้งาน แน่นอนอัตราต่อรองต่ำ แต่ไม่เป็นศูนย์ นั่นคือเหตุผลที่ Log4j 2 มี UuidUtil.getTimeBasedUuid มันจะสร้าง UUID ที่ไม่เหมือนใครเป็นเวลา 8,925 ปีตราบใดที่คุณไม่สร้าง UUIDs / มิลลิวินาทีมากกว่า 10,000 ตัวบนเซิร์ฟเวอร์เดียว
รูปแบบการสร้างดั้งเดิมสำหรับ UUID คือการเชื่อมต่อ UUID เวอร์ชันกับที่อยู่ MAC ของคอมพิวเตอร์ที่สร้าง UUID และมีจำนวน 100-nanosecond ช่วงเวลานับตั้งแต่มีการนำปฏิทินเกรโกเรียนไปทางทิศตะวันตก ด้วยการแสดงจุดเดียวในอวกาศ (คอมพิวเตอร์) และเวลา (จำนวนช่วงเวลา) โอกาสที่จะเกิดการชนกันของค่าต่าง ๆ นั้นไม่มีประสิทธิภาพ
คำตอบหลายคำอธิบายถึงจำนวน UUID ที่จะถูกสร้างขึ้นเพื่อให้มีโอกาส 50% ของการชน แต่โอกาส 50%, 25%, หรือแม้แต่ 1% ของการชนนั้นไม่มีค่าสำหรับแอปพลิเคชั่นที่การชนนั้นจะต้องเป็นไปไม่ได้
โปรแกรมเมอร์ไม่สนใจกิจกรรมอื่นที่ "เป็นไปไม่ได้" ที่สามารถเกิดขึ้นได้หรือไม่?
เมื่อเราเขียนข้อมูลไปยังดิสก์หรือหน่วยความจำและอ่านกลับมาอีกครั้งเราจะรับข้อมูลที่ถูกต้อง เราพึ่งพาการแก้ไขข้อผิดพลาดของอุปกรณ์เพื่อตรวจสอบความเสียหาย แต่โอกาสของความผิดพลาดที่ตรวจไม่พบเป็นจริงประมาณ 2 -50
การใช้มาตรฐานที่คล้ายกันกับ UUID แบบสุ่มจะไม่เหมาะสมหรือไม่ หากคุณทำเช่นนั้นคุณจะพบว่ามีการชนกันที่ "เป็นไปไม่ได้" ในการรวบรวม UUID แบบสุ่มประมาณ 100 พันล้าน (2 36.5 )
นี่เป็นตัวเลขทางดาราศาสตร์ แต่แอปพลิเคชันเช่นการเรียกเก็บเงินแยกรายการในระบบการดูแลสุขภาพแห่งชาติหรือการบันทึกข้อมูลเซ็นเซอร์ความถี่สูงในอุปกรณ์ขนาดใหญ่จำนวนมากอาจกระทบกับขีด จำกัด เหล่านี้ได้ หากคุณกำลังเขียนคู่มือ Hitchhiker คนต่อไปที่ Galaxyอย่าพยายามกำหนด UUID ให้กับแต่ละบทความ!
เนื่องจากคำตอบส่วนใหญ่มุ่งเน้นไปที่ทฤษฎีฉันคิดว่าฉันสามารถเพิ่มบางสิ่งลงในการสนทนาได้โดยการทำแบบทดสอบที่ฉันทำ ในฐานข้อมูลของฉันฉันมีประมาณ 4.5 ล้าน UUID ที่สร้างขึ้นโดยใช้ Java 8 UUID.randomUUID () รายการต่อไปนี้เป็นเพียงบางส่วนที่ฉันค้นพบ:
c0f55f62 -b990-47bc-8caa-f42313669948
c0f55f62 -e81e-4253-8299-00b4322829d5
c0f55f62 -4979-4e87-8cd9-1c556894e2bb
b9ea2498-fb32-40ef-91ef-0ba 00060fe64
be87a209-2114-45b3-9d5a-86d 00060fe64
4a8a74a6-e972-4069-b480-b dea1177b21f
12fb4958-bee2-4c89-8cf8-e dea1177b21f
หากเป็นการสุ่มอย่างแท้จริงความน่าจะเป็นที่มี UUID ที่คล้ายกันประเภทนี้จะต่ำมาก (ดูการแก้ไข) เนื่องจากเราพิจารณาเพียง 4.5 ล้านรายการ ดังนั้นแม้จะฟังก์ชั่นนี้เป็นสิ่งที่ดีในแง่ของการที่ไม่ได้มีการชนกันสำหรับฉันมันไม่ได้ดูเหมือนว่าดีที่สุดเท่าที่มันจะเป็นในทางทฤษฎี
แก้ไข :
ผู้คนจำนวนมากดูเหมือนจะไม่เข้าใจคำตอบนี้ดังนั้นฉันจะชี้แจงประเด็นของฉัน: ฉันรู้ว่าความคล้ายคลึงกันคือ "เล็ก" และห่างไกลจากการชนเต็ม อย่างไรก็ตามฉันแค่ต้องการเปรียบเทียบ UUID.randomUUID () ของ Java กับตัวสร้างตัวเลขสุ่มจริงซึ่งเป็นคำถามจริง
ในตัวสร้างตัวเลขสุ่มจริงความน่าจะเป็นของกรณีสุดท้ายจะอยู่ที่ประมาณ= 0.007% ดังนั้นฉันคิดว่าข้อสรุปของฉันยืนอยู่
สูตรอธิบายไว้ในบทความ wiki นี้ en.wikipedia.org/wiki/Birthday_problem
ฉันเล่นลอตเตอรีเมื่อปีที่แล้วและฉันไม่เคยชนะ .... แต่ดูเหมือนว่าลอตเตอรี่จะมีผู้ชนะ ...
doc: http://tools.ietf.org/html/rfc4122
ประเภทที่ 1: ไม่ได้ใช้งาน การชนกันเป็นไปได้ถ้า uuid ถูกสร้างขึ้นในเวลาเดียวกัน impl อาจเป็นแบบซิงโครไนซ์เพื่อหลีกเลี่ยงปัญหานี้
ประเภทที่ 2: ไม่เคยเห็นการใช้งาน
ประเภทที่ 3: แฮช md5: สามารถชนกันได้ (ไบต์ทางเทคนิค 128 bits-2)
ประเภทที่ 4: สุ่ม: อาจมีการชนกัน (เป็นลอตเตอรี่) โปรดทราบว่า jdk6 impl ไม่ใช้การสุ่ม "ปลอดภัย" ที่ปลอดภัยเพราะอัลกอริทึม PRNG ไม่ได้ถูกเลือกโดยนักพัฒนาและคุณสามารถบังคับให้ระบบใช้อัลตร้า PRNG "ไม่ดี" ดังนั้น UUID ของคุณสามารถคาดเดาได้
ประเภทที่ 5: แฮช sha1: ไม่ได้ใช้งาน: สามารถชนกันได้ (160 ไบเทค -2 ไบต์ทางเทคนิค)
เราใช้ UUID แบบสุ่มของ Java ในแอปพลิเคชันของเรามานานกว่าหนึ่งปีและมีการใช้อย่างกว้างขวาง แต่เราไม่เคยเจอการปะทะกัน