Коли я повинен використовувати uuid.uuid1 () проти uuid.uuid4 () у python?


207

Я розумію відмінності між цими двома документами.

uuid1():
Створіть UUID з ідентифікатора хоста, номера послідовності та поточного часу

uuid4():
Створення випадкового UUID.

Тому uuid1використовує інформацію про машину / послідовність / час для створення UUID. Які плюси та мінуси використання кожного?

Я знаю, що uuid1()може виникнути проблема конфіденційності, оскільки вона базується на машинній інформації. Цікаво, чи є щось більш тонкі при виборі того чи іншого. Я просто uuid4()зараз використовую , оскільки це абсолютно випадковий UUID. Але мені цікаво, чи варто мені використовувати, uuid1щоб зменшити ризик зіткнень.

В основному я шукаю поради щодо кращих практик використання одного проти іншого. Дякую!


3
Ось альтернативний підхід до UUID. Хоча ймовірність зіткнення нескінченно мала, UUID не гарантує унікальності. Щоб гарантувати унікальність, ви можете скористатися складним ключем як [<ідентифікатор системи>, <локальний ідентифікатор>]. Кожна система, яка бере участь у обміні даними, повинна мати свій унікальний ідентифікатор системи, який присвоюється під час налаштування системи або отриманий із загального пулу ідентифікаторів. Місцевий ідентифікатор - це унікальний ідентифікатор у будь-якій конкретній системі. Це передбачає більше клопоту, але гарантує унікальність. Вибачте за офтопік, просто намагаюся допомогти.
oᴉɹǝɥɔ

3
Він не піклується про "проблеми
поваги

Відповіді:


253

uuid1()гарантовано не створює жодних зіткнень (за припущенням, що ви не створюєте занадто багато їх одночасно). Я б не використовував його, якщо важливо, щоб між uuidкомп’ютером та комп'ютером не було зв’язку , оскільки mac-адреса звикає зробити його унікальним на всіх комп’ютерах.

Ви можете створити дублікати, створивши більше 2 14 uuid1 менш ніж за 100ns, але це не є проблемою для більшості випадків використання.

uuid4()створює, як ви сказали, випадковий UUID. Шанс зіткнення дійсно, справді, дуже малий. Досить маленький, що вам не варто про це турбуватися. Проблема полягає в тому, що поганий генератор випадкових чисел робить більш ймовірним виникнення зіткнень.

Ця відмінна відповідь Боба Амана підводить її красиво. (Рекомендую прочитати всю відповідь.)

Відверто кажучи, в одному просторі додатків без шкідливих акторів вимирання всього життя на Землі відбудеться задовго до того, як у вас виникне зіткнення, навіть у версії 4 UUID, навіть якщо ви генеруєте досить багато UUID в секунду.


Вибачте, я прокоментував, не досліджуючи повністю - є біти, зарезервовані для запобігання зіткнення версії 4 uuid з версією 1 uuid. Я видалю свій оригінальний коментар. Дивіться tools.ietf.org/html/rfc4122
Марк Викуп

1
@gs Так, має сенс те, що я читав. uuid1 "більш унікальний", тоді як uuid4 - більш анонімний. Тому в основному використовуйте uuid1, якщо у вас немає причин цього не робити. @mark викуп: Дивовижна відповідь, не знайшла, коли я шукав uuid1 / uuid4. Прямо з вуст коня, здається.
rocketmonkeys

6
uuid1не обов’язково створювати унікальні UUID, якщо ви створюєте кілька в секунду на одному вузлі. Приклад: [uuid.uuid1() for i in range(2)]. Якщо, звичайно, щось дивне не відбувається, чого я пропускаю.
Майкл Міор

1
@Michael: uuid1має порядковий номер (четвертий елемент у вашому прикладі), тому, якщо ви не використаєте всі біти в лічильнику, у вас не буде зіткнення.
Георг Шоллі

3
@Michael: Я спробував дослідити обставини, коли трапляються зіткнення, і додав знайденої мені інформації.
Георг Шолі

32

Один випадок , коли ви можете розглянути питання, uuid1()а не uuid4()є , коли UUID , проводиться на окремих машинах , наприклад , коли кілька онлайн - транзакцій є процес на кількох машин для масштабування цілей.

У такій ситуації, наприклад, ризики виникнення зіткнень через поганий вибір способу ініціалізації генераторів псевдовипадкових чисел, а також потенційно більша кількість вироблених UUID надає більше шансів на можливість створення повторюваних ідентифікаторів.

Інший інтерес uuid1()у цьому випадку полягає в тому, що машина, де спочатку вироблявся кожен GUID, неявно записується (у частині "вузла" UUID). Ця інформація та інформація про час можуть допомогти, якщо тільки з налагодженням.


20

Моя команда просто зіткнулася з проблемою використання UUID1 для сценарію оновлення бази даних, де ми створили ~ 120 000 UUID протягом декількох хвилин. Зіткнення UUID призвело до порушення обмеження первинного ключа.

Ми оновили 100 серверів, але в наших випадках Amazon EC2 ми кілька разів стикалися з цим питанням. Я підозрюю, що погана роздільна здатність та переключення на UUID4 вирішила це для нас.


5

Одне, що слід зазначити при використанні uuid1, якщо ви використовуєте виклик за замовчуванням (не вказуючи clock_seqпараметр), ви маєте шанс зіткнутися зіткненнями: у вас є лише 14 біт випадковості (генерування 18 записів протягом 100ns дає вам приблизно 1% шанс зіткнення див. парадокс / напад на день народження). Проблема ніколи не виникне у більшості випадків використання, але на віртуальній машині з поганою роздільною здатністю годинник вас вкусить.


7
@Guilaume було б дуже корисно побачити приклад належної практики з використання clock_seq....
eric

@Guilaume Як ви розрахували цей шанс у 1%? 14 біт випадковості означає, що зіткнення гарантовано відбудеться, якщо ви генеруєте> = 2 ^ 14 ід на 100нс, а це означає, що 1% шанс зіткнення є, коли ви виробляєте приблизно 163 ід за 100 нс
макс

1
@maks Як я вже казав, варто подивитися на парадокс дня народження .
Гійом

3

Можливо, щось, про що не було сказано, це місцевість.

MAC-адреса або часове впорядкування (UUID1) можуть дозволити собі підвищити продуктивність бази даних, оскільки для сортування чисел ближче один до одного менше, ніж ті, що розподіляються випадковим чином (UUID4) (див. Тут ).

Друга пов'язана проблема полягає в тому, що використання UUID1 може бути корисним при налагодженні, навіть якщо дані про походження втрачені або явно не зберігаються (це, очевидно, суперечить проблемі конфіденційності, згаданої ОП).


1

Окрім прийнятої відповіді, є ще третій варіант, який може бути корисним у деяких випадках:

v1 з випадковим MAC ("v1mc")

Ви можете зробити гібрид між v1 та v4, навмисно генеруючи v1 UUID з випадковою широкомовною MAC-адресою (це дозволено специфікацією v1). Отриманий v1 UUID залежить від часу (як звичайний v1), але йому не вистачає всієї інформації про хости (наприклад, v4). Це також набагато ближче до v4 за його стійкістю до зіткнення: v1mc = 60 біт часу + 61 випадковий біт = 121 унікальний біт; v4 = 122 випадкових біта.

Перше місце, з яким я стикався, була функція uuid_generate_v1mc () Postgres . З тих пір я використовував наступний еквівалент python:

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 (хост-агностик), але гірше (менше бітів, залежність від урадуму тощо). Чи є якісь переваги порівняно з просто uuid4?
rocketmonkeys

Це в першу чергу лише оновлення для тих випадків, коли v1 корисний завдяки своїм часовим якостям, проте бажана більш висока стійкість до зіткнень та конфіденційність хоста. Один з прикладів є основним ключем для бази даних - порівняно з v4, v1 uuids матиме кращу локальність під час запису на диск, матиме більш корисний природний тип тощо. Але якщо у вас є випадок, коли зловмисник прогнозує 2 ** 61 біт - це проблема безпеки (наприклад, як uuid nonce), тоді $ diety так, використовуйте натомість uuid4 (я знаю, що це роблю!). Re: гірше, тому що він використовує urandom, я не впевнений, що ви маєте на увазі - під python, uuid4 () також використовує urandom.
Елі Коллінз

Хороший матеріал, який має сенс. Добре бачити не тільки те, що ти можеш зробити (свій код), а й чому ти цього хочеш. Re: urandom, я маю на увазі, що ви споживаєте в 2 рази випадковість (1 для uuid1, інший для urandom), тому можна швидше використовувати ентропію системи.
rocketmonkeys

Це насправді приблизно вдвічі більше, ніж uuid4: uuid1 () використовує 14 біт для clock_seq, який округляє до 2 байтів urandom. Обгортка uuid1mc використовує 48 біт, які повинні відображати до 6 байтів urandom, для загального споживання urandom (8) на виклик. тоді як uuid4 безпосередньо викликає urandom (16) для кожного дзвінка.
Елі Коллінз
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.