Когда я должен использовать uuid.uuid1 () против uuid.uuid4 () в python?

207

Я понимаю разницу между этими двумя документами.

uuid1():
Генерировать UUID из идентификатора хоста, порядкового номера и текущего времени

uuid4():
Генерировать случайный UUID.

Поэтому uuid1использует информацию о машине / последовательности / времени для генерации UUID. Каковы плюсы и минусы использования каждого?

Я знаю, что uuid1()может иметь проблемы с конфиденциальностью, так как она основана на информации о машине. Интересно, есть ли что-нибудь более тонкое при выборе одного или другого. Я просто использую uuid4()прямо сейчас, так как это совершенно случайный UUID. Но мне интересно, должен ли я использовать, uuid1чтобы уменьшить риск столкновений.

По сути, я ищу советы людей по передовым методам использования одного против другого. Спасибо!

rocketmonkeys
источник
3
Вот альтернативный подход к UUID. Хотя вероятность столкновения бесконечно мала, UUID не гарантирует уникальность. Чтобы гарантировать уникальность, вы можете использовать составной ключ как [<системный идентификатор>, <локальный идентификатор>]. Каждая система, участвующая в обмене данными, должна иметь свой собственный уникальный идентификатор системы, назначенный во время настройки системы или полученный из общего пула идентификаторов. Локальный идентификатор - это уникальный идентификатор в любой конкретной системе. Это требует больше хлопот, но гарантирует уникальность. Извините за оффтоп, просто пытаюсь помочь.
oᴉɹǝɥɔ
3
Он не заботится о «проблемах конфиденциальности», о которых он говорил
Shrey

Ответы:

253

uuid1()гарантированно не вызовет никаких коллизий (при условии, что вы не создаете слишком много их одновременно). Я бы не стал использовать его, если важно, чтобы между uuidкомпьютером и компьютером не было связи , поскольку MAC-адрес используется для того, чтобы сделать его уникальным для всех компьютеров.

Вы можете создать дубликаты, создав более 2 14 uuid1 менее чем за 100 нс, но это не проблема для большинства случаев использования.

uuid4()генерирует, как вы сказали, случайный UUID. Вероятность столкновения действительно, на самом деле, на самом деле мало. Достаточно маленький, чтобы не беспокоиться об этом. Проблема в том, что плохой генератор случайных чисел повышает вероятность возникновения коллизий.

Этот превосходный ответ Боба Амана хорошо подводит итог. (Я рекомендую прочитать весь ответ.)

Честно говоря, в одном пространстве приложений без злонамеренных акторов вымирание всей жизни на Земле произойдет задолго до того, как вы столкнетесь, даже с UUID версии 4, даже если вы генерируете довольно много UUID в секунду.

Георг Шолли
источник
Извините, я прокомментировал без подробного исследования - есть биты, зарезервированные для предотвращения столкновения версии 4 с идентификатором uuid версии 1. Я удалю свой оригинальный комментарий. См. Tools.ietf.org/html/rfc4122
Марк Рэнсом
1
@gs Да, имеет смысл то, что я читал. uuid1 «более уникален», а uuid4 более анонимен. Так что в основном используйте uuid1, если у вас нет причин не делать этого. @ Марк выкуп: Потрясающий ответ, не пришел, когда я искал uuid1 / uuid4. Кажется, прямо изо рта лошади.
rocketmonkeys
6
uuid1не обязательно будет генерировать уникальные UUID, если вы производите несколько в секунду на одном узле. Пример: [uuid.uuid1() for i in range(2)]. Если, конечно, не происходит что-то странное, что я скучаю.
Майкл Миор
1
@Michael: uuid1имеет порядковый номер (4-й элемент в вашем примере), поэтому, если вы не используете все биты в счетчике, вы не столкнетесь.
Георг Шолли
3
@Michael: я попытался исследовать обстоятельства, когда происходят столкновения, и добавил информацию, которую нашел.
Георг Шолли
32

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

В такой ситуации, например, риск возникновения коллизий из-за неправильного выбора способа инициализации генераторов псевдослучайных чисел, а также потенциально более высокого числа создаваемых UUID делает более вероятной возможность создания дублированных идентификаторов.

Другой интерес uuid1()в этом случае заключается в том, что машина, на которой каждый GUID был изначально создан, записывается неявно (в части «узел» UUID). Это и информация о времени, может помочь, если только с отладкой.

MJV
источник
20

Моя команда только что столкнулась с проблемой при использовании UUID1 для сценария обновления базы данных, где мы сгенерировали ~ 120k UUID за пару минут. Конфликт UUID привел к нарушению ограничения первичного ключа.

Мы обновили сотни серверов, но в наших экземплярах Amazon EC2 мы несколько раз сталкивались с этой проблемой. Я подозреваю, что плохое разрешение часов и переход на UUID4 решили это за нас.

Маттиас Лагергрен
источник
5

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

Гийом
источник
7
@Guilaume было бы очень полезно увидеть пример хорошей практики с использованием clock_seq....
Эрик
@Guilaume Как вы рассчитали этот шанс 1%? 14 битов случайности означают, что столкновение гарантированно произойдет, если вы сгенерируете> = 2 ^ 14 идентификаторов на 100 нс, и это означает, что 1% вероятности столкновения происходит, когда вы производите примерно 163 идентификатора на 100 нс
макс
1
@maks Как я уже сказал, тебе стоит взглянуть на парадокс дня рождения .
Гийом,
3

Возможно, что-то, что не было упомянуто, - это местность.

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

Вторая связанная с этим проблема заключается в том, что использование UUID1 может быть полезно при отладке, даже если исходные данные потеряны или не сохранены в явном виде (это явно противоречит проблеме конфиденциальности, упомянутой в OP).

CZ
источник
1

Помимо принятого ответа, в некоторых случаях может быть полезен третий вариант:

v1 со случайным MAC («v1mc»)

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

Первое, с чем я столкнулся - это функция Postgres uuid_generate_v1mc () . С тех пор я использовал следующий эквивалент 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 (независимый от хоста), но хуже (меньше битов, зависимость от urandom и т. Д.). Есть ли какие-то преимущества по сравнению с просто uuid4?
rocketmonkeys
Это в первую очередь просто обновление для случаев, когда v1 полезен из-за его качеств, основанных на времени, но желательно более сильное сопротивление коллизиям и конфиденциальность хоста. Одним из примеров является первичный ключ для базы данных - по сравнению с v4, v1 uuids будет иметь лучшую локальность при записи на диск, иметь более полезную естественную сортировку и т. Д. Но если у вас есть случай, когда злоумышленник предсказывает 2 ** 61 бит - это проблема безопасности (например, как uuid, одноразовый номер), тогда $ diety yes, используйте вместо этого 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) для каждого вызова.
Эли Коллинз