เมื่อใดที่ฉันควรใช้ uuid.uuid1 () กับ uuid.uuid4 () ใน python


207

ฉันเข้าใจความแตกต่างระหว่างทั้งสองจากเอกสาร

uuid1():
สร้าง UUID จาก ID โฮสต์หมายเลขลำดับและเวลาปัจจุบัน

uuid4():
สร้าง UUID แบบสุ่ม

ดังนั้นuuid1ใช้ข้อมูลเครื่อง / ลำดับ / เวลาเพื่อสร้าง UUID ข้อดีและข้อเสียของการใช้แต่ละข้อมีอะไรบ้าง

ฉันรู้ว่าuuid1()อาจมีข้อกังวลเกี่ยวกับข้อมูลส่วนบุคคลเนื่องจากเป็นข้อมูลของเครื่องจักร ฉันสงสัยว่ามีความละเอียดอ่อนกว่านี้อีกหรือไม่เมื่อเลือกอย่างใดอย่างหนึ่ง ฉันเพิ่งใช้uuid4()ตอนนี้เนื่องจากเป็น UUID ที่สุ่มอย่างสมบูรณ์ แต่ฉันสงสัยว่าควรใช้uuid1เพื่อลดความเสี่ยงของการชนหรือไม่

โดยพื้นฐานแล้วฉันกำลังมองหาเคล็ดลับของผู้คนสำหรับแนวทางปฏิบัติที่ดีที่สุดเกี่ยวกับการใช้แบบเทียบกับแบบอื่น ขอบคุณ!


3
นี่คือวิธีการอื่นในการ UUID แม้ว่าโอกาสที่จะเกิดการชนจะเป็น UUID ที่น้อยที่สุด แต่ก็ไม่รับประกันถึงความเป็นเอกลักษณ์ เพื่อรับประกันความเป็นเอกลักษณ์คุณอาจต้องการใช้คีย์ผสมเป็น [<system id>, <local id>] แต่ละระบบที่มีส่วนร่วมในการแบ่งปันข้อมูลจะต้องมี ID เฉพาะของตัวเองของระบบทั้งที่ได้รับมอบหมายในระหว่างการตั้งค่าระบบหรือได้มาจากกลุ่มทั่วไปของ ID Local id เป็นรหัสเฉพาะภายในระบบใด ๆ สิ่งนี้เกี่ยวข้องกับความยุ่งยากมากขึ้น แต่รับประกันความเป็นเอกลักษณ์ ขออภัยสำหรับ offtopic เพียงพยายามช่วย
oᴉɹǝɥɔ

3
ไม่ดูแล "ความกังวลเรื่องความเป็นส่วนตัว" ที่เขากล่าวถึง
Shrey

คำตอบ:


253

uuid1()รับประกันได้ว่าจะไม่สร้างการชนใด ๆ (ภายใต้สมมติฐานที่คุณไม่ได้สร้างไว้มากเกินไปในเวลาเดียวกัน) ฉันจะไม่ใช้มันหากเป็นสิ่งสำคัญที่ไม่มีการเชื่อมต่อระหว่างuuidคอมพิวเตอร์กับคอมพิวเตอร์เนื่องจากที่อยู่ mac เริ่มชินกับความเป็นเอกลักษณ์ของคอมพิวเตอร์

คุณสามารถสร้างรายการที่ซ้ำกันโดยสร้างมากกว่า 2 14 uuid1 ในเวลาน้อยกว่า 100ns แต่นี่ไม่ใช่ปัญหาสำหรับกรณีการใช้งานส่วนใหญ่

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

คำตอบที่ยอดเยี่ยมนี้โดยบ็อบอามันสรุปได้อย่างดี (ฉันแนะนำให้อ่านคำตอบทั้งหมด)

ตรงไปตรงมาในพื้นที่แอปพลิเคชั่นเดียวที่ไม่มีนักแสดงที่เป็นอันตรายการสูญพันธุ์ของชีวิตทั้งหมดบนโลกจะเกิดขึ้นนานก่อนที่คุณจะปะทะกันแม้ใน UUID เวอร์ชัน 4 แม้ว่าคุณจะสร้าง UUIDs ค่อนข้างน้อยต่อวินาที


ขออภัยฉันแสดงความคิดเห็นโดยไม่ต้องค้นคว้าอย่างเต็มที่ - มีบิตที่สงวนไว้เพื่อป้องกันเวอร์ชัน 4 uuid ไม่ให้ชนกับเวอร์ชัน 1 uuid ฉันจะลบความคิดเห็นดั้งเดิมของฉัน ดูtools.ietf.org/html/rfc4122
Mark Ransom

1
@gs ใช่เหมาะสมกับสิ่งที่ฉันอ่าน uuid1 เป็น "ที่ไม่ซ้ำกันมากขึ้น" ในขณะที่ uuid4 ไม่ระบุชื่อมากขึ้น ดังนั้นโดยทั่วไปให้ใช้ uuid1 ถ้าคุณไม่มีเหตุผล @mark ransom: คำตอบที่น่ากลัวไม่ได้เกิดขึ้นเมื่อฉันค้นหา uuid1 / uuid4 ดูเหมือนตรงจากปากม้า
rocketmonkeys

6
uuid1จะไม่จำเป็นต้องสร้าง UUID ที่ไม่ซ้ำกันหากคุณสร้างหลาย ๆ ต่อวินาทีบนโหนดเดียวกัน ตัวอย่าง: [uuid.uuid1() for i in range(2)]. เว้นแต่จะมีบางสิ่งที่แปลกประหลาดเกิดขึ้นที่ฉันหายไป
Michael Mior

1
@Michael: uuid1มีหมายเลขลำดับ (องค์ประกอบที่ 4 ในตัวอย่างของคุณ) ดังนั้นถ้าคุณไม่ใช้บิตทั้งหมดในตัวนับคุณจะไม่มีการชนกัน
Georg Schölly

3
@Michael: ฉันพยายามค้นคว้าสถานการณ์เมื่อมีการชนเกิดขึ้นและได้เพิ่มข้อมูลที่ฉันพบ
Georg Schölly

32

อินสแตนซ์หนึ่งเมื่อคุณอาจพิจารณาuuid1()มากกว่าuuid4()คือเมื่อมีการผลิต UUID บนเครื่องที่แยกต่างหากตัวอย่างเช่นเมื่อการทำธุรกรรมออนไลน์หลายรายการดำเนินการบนเครื่องหลายเครื่องเพื่อปรับขนาด

ในสถานการณ์เช่นนี้ความเสี่ยงของการชนเนื่องจากตัวเลือกที่ไม่ดีในวิธีที่เครื่องกำเนิดไฟฟ้าแบบหลอกหลอกถูกกำหนดค่าเริ่มต้นเป็นต้นและจำนวน UUID ที่สร้างขึ้นจำนวนมากอาจทำให้เกิดความเป็นไปได้ในการสร้างรหัสซ้ำ

สิ่งที่น่าสนใจอีกอย่างหนึ่งuuid1()คือในกรณีที่เครื่องที่ผลิต GUID แต่ละครั้งนั้นถูกบันทึกไว้โดยปริยาย (ในส่วน "node" ของ UUID) ข้อมูลนี้และเวลาอาจช่วยได้เฉพาะเมื่อทำการดีบักเท่านั้น


20

ทีมของฉันมีปัญหาในการใช้ UUID1 สำหรับสคริปต์อัปเกรดฐานข้อมูลที่เราสร้าง UUIDs ประมาณ 120k ภายในสองสามนาที การชนกันของ UUID นำไปสู่การละเมิดข้อ จำกัด คีย์หลัก

เราได้อัพเกรดเซิร์ฟเวอร์ 100s แต่ใน Amazon EC2 อินสแตนซ์ของเราเราพบปัญหานี้สองสามครั้ง ฉันสงสัยว่าความละเอียดสัญญาณนาฬิกาต่ำและการเปลี่ยนเป็น UUID4 แก้ปัญหาได้สำหรับเรา


5

สิ่งหนึ่งที่ควรทราบเมื่อใช้uuid1หากคุณใช้การโทรเริ่มต้น (โดยไม่ให้clock_seqพารามิเตอร์) คุณมีโอกาสที่จะเกิดการชน: คุณมีการสุ่มเพียง 14 บิต (การสร้าง 18 รายการภายใน 100ns ให้โอกาส 1% ของการชนกันโดยประมาณ วันเกิดความขัดแย้ง / การโจมตี) ปัญหาจะไม่เกิดขึ้นในกรณีการใช้งานส่วนใหญ่ แต่บนเครื่องเสมือนที่มีความละเอียดสัญญาณนาฬิกาไม่ดีมันจะกัดคุณ


7
@Guilaume มันจะมีประโยชน์มากที่จะเห็นตัวอย่างของการปฏิบัติที่ดีในการใช้clock_seq....
eric

@Guilaume คุณคำนวณโอกาสนี้ได้ 1% อย่างไร การสุ่มแบบสุ่ม 14 บิตหมายความว่าการชนจะรับประกันว่าจะเกิดขึ้นหากคุณสร้าง> = 2 ^ 14 รหัสต่อ 100ns และนั่นหมายความว่าโอกาส 1% ของการชนคือเมื่อคุณผลิต 163 รหัสต่อ 100 ns
maks

1
@maks ที่ผมกล่าวว่าคุณควรจะดูที่ความขัดแย้งวันเกิด
Guillaume

3

บางทีสิ่งที่ไม่ได้กล่าวถึงอาจเป็นของท้องถิ่น

ที่อยู่ MAC หรือการสั่งซื้อตามเวลา (UUID1) สามารถเพิ่มประสิทธิภาพของฐานข้อมูลได้เนื่องจากการเรียงลำดับตัวเลขใกล้กันน้อยกว่าการกระจายแบบสุ่ม (UUID4) (ดูที่นี่ )

ปัญหาที่สองที่เกี่ยวข้องคือการใช้ UUID1 อาจมีประโยชน์ในการแก้ไขข้อบกพร่องแม้ว่าข้อมูลต้นทางจะสูญหายหรือไม่ได้จัดเก็บไว้อย่างชัดเจน


1

นอกจากคำตอบที่ยอมรับแล้วยังมีตัวเลือกที่สามที่สามารถเป็นประโยชน์ในบางกรณี:

v1 กับ Random MAC ("v1mc")

คุณสามารถสร้างไฮบริดระหว่าง v1 & v4 โดยจงใจสร้าง v1 UUIDs โดยมีที่อยู่ MAC ออกอากาศแบบสุ่ม (อนุญาตโดยข้อกำหนด v1) ผลลัพธ์ UUID v1 ขึ้นอยู่กับเวลา (เช่นปกติ v1) แต่ขาดข้อมูลเฉพาะของโฮสต์ทั้งหมด (เช่น v4) นอกจากนี้ยังใกล้เคียงกับ v4 ในการต้านทานการชน: v1mc = 60 บิต + 61 บิตสุ่ม = 121 บิตที่ไม่ซ้ำกัน v4 = 122 บิตสุ่ม

สถานที่แรกที่ฉันพบนี่คือฟังก์ชั่นPostgres ' uuid_generate_v1mc () ฉันได้ใช้เทียบเท่ากับไพ ธ อนต่อไปนี้:

from os import urandom
from uuid import uuid1
_int_from_bytes = int.from_bytes  # py3 only

def uuid1mc():
    # NOTE: The constant here is required by the UUIDv1 spec...
    return uuid1(_int_from_bytes(urandom(6), "big") | 0x010000000000)

(หมายเหตุ: ฉันมีรุ่นที่ยาวกว่าและเร็วกว่าที่สร้างวัตถุ UUID โดยตรงสามารถโพสต์ได้ถ้าใครต้องการ)


ในกรณีที่มีปริมาณการโทร / วินาทีจำนวนมากสิ่งนี้มีความเป็นไปได้ที่จะหมดการสุ่มของระบบ คุณสามารถใช้randomโมดูลstdlib แทน (มันอาจจะเร็วกว่าด้วย) แต่ต้องระวัง: ใช้เวลาเพียงไม่กี่ร้อย UUID ก่อนที่ผู้โจมตีจะสามารถกำหนดสถานะ RNG ได้และบางส่วนจึงคาดการณ์ UUID ในอนาคต

import random
from uuid import uuid1

def uuid1mc_insecure():
    return uuid1(random.getrandbits(48) | 0x010000000000)

ดูเหมือนว่าวิธีนี้คือ "ชอบ" v4 (โฮสต์ - ผู้ไม่เชื่อเรื่องพระเจ้า) แต่ที่เลวร้ายยิ่ง (บิตน้อยพึ่งพาพึ่งพา urandom ฯลฯ ) มีข้อดีอะไรบ้างเมื่อเทียบกับ uuid4
rocketmonkeys

นี่เป็นเพียงการอัปเกรดสำหรับกรณีที่ v1 มีประโยชน์สำหรับคุณสมบัติตามเวลา แต่ต้องการความต้านทานการชนที่แข็งแกร่งขึ้นและต้องการความเป็นส่วนตัวของโฮสต์ ตัวอย่างหนึ่งคือคีย์หลักสำหรับฐานข้อมูล - เมื่อเทียบกับ v4, v1 uuids จะมีตำแหน่งที่ดีกว่าเมื่อเขียนลงดิสก์มีการเรียงลำดับตามธรรมชาติที่มีประโยชน์มากกว่า ฯลฯ แต่ถ้าคุณมีกรณีที่ผู้โจมตีคาดการณ์ 2 ** 61 bits เป็นปัญหาด้านความปลอดภัย (เช่น uuid a nonce) จากนั้น $ diety ใช่ให้ใช้ uuid4 แทน (ฉันรู้ว่าฉันทำ!) Re: แย่ลงเพราะใช้ urandom ฉันไม่แน่ใจว่าคุณหมายถึงอะไร - ภายใต้ python, uuid4 () ใช้ urandom ด้วย
Eli Collins

สิ่งที่ดีที่เหมาะสม เป็นการดีที่จะได้เห็นไม่ใช่เพียงแค่สิ่งที่คุณสามารถทำได้ (รหัสของคุณ) แต่ยังเป็นสาเหตุที่คุณต้องการ Re: urandom ฉันหมายถึงว่าคุณใช้ความสุ่ม 2 เท่า (1 สำหรับ uuid1 และอีกอันสำหรับ urandom) ดังนั้นคุณสามารถใช้เอนโทรปีของระบบได้เร็วขึ้น
rocketmonkeys

จริง ๆ แล้วประมาณครึ่งหนึ่งเท่ากับ uuid4: uuid1 () ใช้ 14 บิตสำหรับ clock_seq ซึ่งปัดเศษได้สูงสุด 2 ไบต์ของ urandom wrapper uuid1mc ใช้ 48 บิตซึ่งควรแมปกับ urandom 6 ไบต์สำหรับจำนวนทั้งหมดของ urandom (8) ที่ใช้ต่อการโทรหนึ่งครั้ง ในขณะที่ uuid4 จะเรียกใช้ urandom (16) สำหรับการโทรทุกครั้งโดยตรง
Eli Collins
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.