Когда вы действительно вынуждены использовать UUID как часть дизайна?

124

Я не вижу смысла в UUID . Я знаю, что вероятность столкновения фактически равна нулю , но фактически ноль даже близко не к невозможному.

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

UUID для меня пахнет глобальными переменными. Есть много способов, которыми глобальные переменные упрощают дизайн, но это просто ленивый дизайн.

Pyrolistical
источник
23
Шансы на неудачу у всего ненулевые. Я бы сосредоточился на гораздо более вероятных проблемах (т.е. почти на чем угодно, о чем вы можете подумать), чем на столкновении UUID,
DanSingerman
16
На самом деле, «эффективно ноль» очень близко к невозможному.
mqp
21
Нет, на самом деле это бесконечно далеко от невозможного
Pyrolistical
32
@Pyrolistical когда вы начинаете использовать такие слова, как «бесконечность», вы покинули мир разработки программного обеспечения. Теория информатики - это совсем другое обсуждение, чем написание реального программного обеспечения.
Rex M
2
Я закрою в основном потому, что git sha1 убедил меня в
полезности

Ответы:

619

Я написал генератор / парсер UUID для Ruby, поэтому считаю себя достаточно хорошо осведомленным по этому поводу. Существует четыре основных версии UUID:

UUID версии 4 - это, по сути, всего лишь 16 байтов случайности, извлеченные из криптографически безопасного генератора случайных чисел, с некоторым изменением битов для идентификации версии и варианта UUID. Они крайне маловероятны, чтобы столкнуться, но это может произойти, если используется ГПСЧ или если вам просто действительно, очень, очень, очень, очень не повезло.

UUID версии 5 и версии 3 используют хэш-функции SHA1 и MD5 соответственно, чтобы объединить пространство имен с частью уже уникальных данных для генерации UUID. Это, например, позволит вам создать UUID из URL-адреса. Коллизии здесь возможны только в том случае, если основная хеш-функция также имеет коллизию.

UUID версии 1 являются наиболее распространенными. Они используют MAC-адрес сетевой карты (который, если он не подделан, должен быть уникальным), плюс временная метка, а также обычная перестановка битов для генерации UUID. В случае машины, у которой нет MAC-адреса, 6 байтов узла генерируются криптографически безопасным генератором случайных чисел. Если два UUID генерируются последовательно достаточно быстро, чтобы метка времени совпадала с предыдущим UUID, метка времени увеличивается на 1. Коллизии не должны происходить, если не произойдет одно из следующих событий: MAC-адрес подделан; Одна машина, на которой запущены два разных приложения, генерирующих UUID, генерирует UUID в один и тот же момент; Две машины без сетевой карты или без доступа уровня пользователя к MAC-адресу получают одинаковую случайную последовательность узлов и генерируют UUID в один и тот же момент;

На самом деле ни одно из этих событий не происходит случайно в пространстве идентификаторов одного приложения. Если вы не принимаете идентификаторы, скажем, в масштабе всего Интернета или в ненадежной среде, где злоумышленники могут сделать что-то плохое в случае конфликта идентификаторов, вам не о чем беспокоиться. Очень важно понимать, что если вам случится сгенерировать тот же UUID версии 4, что и я, в большинстве случаев это не имеет значения. Я сгенерировал идентификатор в совершенно другом месте, отличном от вашего. Мое приложение никогда не узнает о столкновении, поэтому столкновение не имеет значения. Откровенно говоря, в едином пространстве приложений без злонамеренных субъектов исчезновение всего живого на Земле произойдет задолго до того, как вы столкнетесь, даже с UUID версии 4, даже если вы

Кроме того, 2 ^ 64 * 16 составляет 256 эксабайт. Например, вам нужно будет хранить идентификаторы на 256 экзабайт, прежде чем у вас будет 50% вероятность конфликта идентификаторов в одном пространстве приложения.

Боб Аман
источник
8
Это, безусловно, лучшее объяснение. Я не знаю, почему это не голосование наверху. Престижность вам Sporkmonger.
Брэд Баркер,
1
@Chamnap Я написал UUIDTools. UUID могут быть преобразованы в целое число или их необработанную байтовую форму и будут значительно меньше, чем двоичные.
Боб Аман,
1
@Chamnap uuid.rawпредоставит вам байтовую строку. hashМетод не является полезным для вас. Он используется для хэш-таблиц и операций сравнения внутри Ruby. Все методы преобразования в различные представления UUID и обратно определены как методы класса и должны иметь префикс "parse".
Боб Аман
3
@BobAman в 1990 году у меня было 12 столкновений UUID в системе Aegis, оказалось, что это неисправный FPU, но я подумал, что дам вам знать, что это может произойти (хотя за последние 30 с лишним лет программирования не произошло ничего, кроме этого) , Хорошее объяснение, кстати, теперь это мой фактический пост UUID для раздачи людям :)
GMasucci
2
@kqr Вы абсолютно правы, что это проблема дня рождения, однако для n-битного кода проблема парадокса дня рождения сокращается до 2 ^ (n / 2), что в данном случае составляет 2 ^ 64, как указано в моем ответе ,
Боб Аман
69

То, что покупают UUID, что очень сложно сделать иначе, - это получить уникальный идентификатор без необходимости консультироваться или согласовывать с центральным органом . Общая проблема возможности получить такую ​​вещь без какой-либо управляемой инфраструктуры - это проблема, которую решают UUID.

Я читал, что в соответствии с парадоксом дня рождения вероятность столкновения UUID составляет 50% после создания 2 ^ 64 UUID. Теперь 2 ^ 64 - довольно большое число, но вероятность столкновения 50% кажется слишком рискованной (например, сколько UUID должно существовать, чтобы вероятность столкновения 5% - даже это кажется слишком большой вероятностью) ,

Проблема с этим анализом двоякая:

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

  2. строго говоря, UUID должны быть уникальными только среди набора других UUID, с которыми они могут сравниваться. Если вы генерируете UUID для использования в качестве ключа базы данных, не имеет значения, используется ли где-то еще в злой альтернативной вселенной, что тот же UUID используется для идентификации COM-интерфейса. Точно так же не вызовет путаницы, если на Альфа-Центавре есть кто-то (или что-то еще) по имени Майкл Бёрр.

Майкл Берр
источник
1
Конкретный пример? COM / DCE UUID - нет полномочий для их назначения, и никто не хотел брать на себя ответственность и / или никто не хотел, чтобы там были полномочия. Распределенные базы данных, которые не имеют надежных ссылок и мастера.
Майкл Берр,
3
Более конкретный пример - банковское приложение. Установлено несколько центров обработки данных, по одному для каждой страны, при этом в каждом центре обработки данных есть БД. Множественные установки предназначены для соблюдения различных правил. Там может быть только одна запись о клиенте во всем наборе для каждого клиента .....
Vineet Рейнольдс
(Продолжение предыдущего комментария) У вас должен быть центральный сервер для генерации идентификатора клиента для общих целей отчетности и отслеживания (для всех установок) или чтобы отдельные установки генерировали UUID для использования в качестве идентификаторов клиентов (очевидно, что UUID не могут использоваться, как в в отчетах).
Vineet Reynolds
К тому времени, когда у вас есть 50% шанс дублирования, вы уже тонете. Кто-нибудь укажет объем, необходимый для достижения вероятности 0,0000001%. Несколько баз данных с автоинкрементом, начиная с 1 до n и увеличиваясь на n каждый раз, эффективно решают одну и ту же проблему.
Гордон
2
Шансы получить дубликат НАМНОГО ниже, чем вероятность того, что центральный орган
потерпит
33

Шансы на неудачу у всего ненулевые. Я бы сосредоточился на гораздо более вероятных проблемах (т.е. почти на всем, о чем вы можете подумать), чем на столкновении UUID

DanSingerman
источник
Добавлено в качестве ответа на запрос
Pyrolistical
16

Акцент на «разумно» или, как вы выразились, «эффективно»: достаточно хорошо, как устроен реальный мир. Объем вычислительной работы, необходимой для преодоления этого разрыва между «практически уникальным» и «действительно уникальным», огромен. Уникальность - это кривая с убывающей доходностью. В какой-то момент на этой кривой проходит грань между тем, где «достаточно уникальное» все еще доступно, и затем мы изгибаемся ОЧЕНЬ круто. Стоимость добавления большей уникальности становится довольно большой. Бесконечная уникальность имеет бесконечную цену.

UUID / GUID, условно говоря, вычислительно быстрый и простой способ сгенерировать идентификатор, который можно разумно считать универсально уникальным. Это очень важно во многих системах, которым необходимо интегрировать данные из ранее не подключенных систем. Например: если у вас есть система управления контентом, которая работает на двух разных платформах, но в какой-то момент вам нужно импортировать контент из одной системы в другую. Вы не хотите, чтобы идентификаторы менялись, поэтому ваши ссылки между данными из системы A остаются нетронутыми, но вы не хотите никаких конфликтов с данными, созданными в системе B. UUID решает эту проблему.

Рекс М
источник
Решение. Не поленитесь и обновите ссылки. Сделай это правильно.
Pyrolistical
8
Это не имеет ничего общего с ленью - если политика такова, что идентификатор элемента считается постоянным и неизменяемым, то идентификатор не изменяется. Итак, вы хотите, чтобы идентификаторы были уникальными с самого начала, и вы хотите сделать это, не требуя, чтобы все системы были каким-либо образом связаны с самого начала.
Майкл Берр,
Тогда вам нужен контекст. Если у вас есть две группы уникальных идентификаторов, которые могут конфликтовать, вам потребуется высокий уровень контекста, чтобы разделить их
Пиролитический,
23
Или вы можете просто построить систему, использующую UUID, и отправить ее, продать, заработать миллион долларов и никогда не услышать ни одной жалобы на столкновение двух идентификаторов, потому что этого не произойдет.
Rex M
16

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

Это может помочь в разрешении репликации базы данных и т. Д.

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

В любом случае, слово о вероятности столкновения взято из Википедии:

Чтобы представить эти цифры в перспективе, ежегодный риск столкновения с метеоритом оценивается в один шанс из 17 миллиардов, что эквивалентно шансам создания нескольких десятков триллионов UUID в год и наличия одного дубликата. Другими словами, только после генерации 1 миллиарда UUID каждую секунду в течение следующих 100 лет вероятность создания всего одного дубликата составит около 50%.

Джон Гитцен
источник
4
Просто не позволяйте офлайн-пользователям генерировать ключи. Назначьте временные ключи до тех пор, пока система не перейдет в онлайн, чтобы можно было сгенерировать настоящие ключи.
Pyrolistical
На мой взгляд, это очень полезный ответ ... я сам собирался провести некую аналогию с вероятностью, так как казалось, что ОП не совсем понял ее значение, но вы, похоже, это сделали.
Noldorin
Я понимаю, что вероятность фактически равна нулю. Для меня использование UUID - это ленивая
идея
Это достаточно справедливо, если вы видите, что низкая вероятность должна учитываться даже в самых экстремальных обстоятельствах, как я теперь предполагаю, что вы это делаете.
Noldorin
13

Классический пример - репликация между двумя базами данных.

DB (A) вставляет запись с int ID 10 и в то же время DB (B) создает запись с идентификатором in 10. Это коллизия.

С UUID этого не произойдет, поскольку они не будут совпадать. (почти наверняка)

Джонно Нолан
источник
1
Хорошо, тогда заставьте БД A использовать четный идентификатор, а БД B использовать нечетные идентификаторы. Готово, UUID нет.
Pyrolistical
2
С тремя DB используйте 3 кратные LOL
Jhonny D. Cano -Leftware-
20
Если вы используете кратные 2/3 / любой другой, что произойдет, когда вы добавите новый сервер в микс позже? Вы должны скоординировать переключение так, чтобы вы использовали кратные n + 1 на новом сервере, и переместите все старые серверы на новый алгоритм, и вам нужно выключить все, пока вы это делаете, чтобы избежать коллизий во время переключатель алгоритма. Или ... вы можете просто использовать UUID, как ВСЕ ЕЩЕ.
Боб Аман,
3
Это даже хуже, потому что как бы вы отличили количество, кратное 2, от кратного 4? Или кратные 3 по сравнению с кратными 6? Фактически, вам придется придерживаться кратных простых чисел. Blech! Просто используйте UUID, он работает. Microsoft, Apple и многие другие полагаются на них и доверяют им.
sidewinderguy
2
@sidewinderguy, мы верим в GUID! :)
Рон Кляйн
13

Также существует ненулевая вероятность того, что каждая частица вашего тела одновременно пройдет через стул, на котором вы сидите, и вы внезапно окажетесь сидящим на полу.

Вы беспокоитесь об этом?

user21714
источник
7
Конечно, нет, это не то, что я могу контролировать, но дизайн я могу.
Pyrolistical
4
@Pyrolistical Is , что на самом деле, я имею в виду на самом деле причина вам не беспокоиться об этом? Тогда ты довольно странный. Более того, ты не прав. Вы можете это контролировать. Набрав несколько килограммов, вы значительно уменьшите вероятность такого события. Считаете ли вы, что вам следует набрать вес? :-)
Veky
8

У меня есть схема, позволяющая избежать UUID. Настройте где-нибудь сервер и сделайте так, чтобы каждый раз, когда какое-то программное обеспечение запрашивало универсальный уникальный идентификатор, они связывались с этим сервером, и он выдавал его. Просто!

За исключением того, что с этим связаны некоторые реальные практические проблемы, даже если мы проигнорируем откровенную злобу. В частности, этот сервер может выйти из строя или стать недоступным из части Интернета. Работа с отказом сервера требует репликации, и это очень сложно сделать правильно (см. Литературу по алгоритму Paxos, чтобы узнать, почему достижение консенсуса неудобно), и это тоже довольно медленно. Более того, если все серверы недоступны из определенной части сети, ни один из клиентов, подключенных к этой подсети, не сможет что-либо сделать, потому что все они будут ждать новых идентификаторов.

Итак ... используйте простой вероятностный алгоритм для их генерации, который вряд ли выйдет из строя в течение всего жизненного цикла Земли, или (профинансируйте и) создайте основную инфраструктуру, которая будет представлять собой PITA развертывания и будет иметь частые сбои. Я знаю, какой я бы выбрал.

Donal Fellows
источник
2
Фактически, весь смысл изобретения UUID состоял в том, чтобы избежать вашего подхода. Если вы исследуете историю UUID, то увидите, что она восходит к самым ранним экспериментам по созданию сложных и значимых компьютерных сетей. Они знали, что сети по своей сути ненадежны и сложны. UUID были ответом на вопрос о том, как координировать данные между компьютерами, когда вы знали, что они не могут постоянно обмениваться данными.
Basil Bourque
7
@BasilBourque Я использовал сарказм в первом абзаце на случай, если это не было очевидно.
Donal Fellows
5

я не понимаю всех разговоров о вероятности столкновения. Меня не волнуют столкновения. Хотя я забочусь о производительности.

https://dba.stackexchange.com/a/119129/33649

UUID - это катастрофа для производительности очень больших таблиц. (200 КБ строк не очень большие.)

Ваш № 3 действительно плох, когда CHARCTER SET - utf8 - CHAR (36) занимает 108 байтов!

UUID (GUID) очень "случайны". Использование их в качестве УНИКАЛЬНЫХ или ПЕРВИЧНЫХ ключей для больших таблиц очень неэффективно. Это связано с тем, что вам нужно перемещаться по таблице / индексу каждый раз, когда вы ВСТАВЛЯЕТЕ новый UUID или ВЫБИРАЕТЕ по UUID. Когда таблица / индекс слишком велики, чтобы поместиться в кеш (см. Innodb_buffer_pool_size, который должен быть меньше ОЗУ, обычно 70%), «следующий» UUID может не кэшироваться, следовательно, медленное обращение к диску. Когда таблица / индекс в 20 раз больше кеша, кэшируется только 1/20 (5%) попаданий - вы ограничены вводом-выводом.

Итак, не используйте UUID, если только

у вас есть «маленькие» таблицы или они вам действительно нужны из-за генерации уникальных идентификаторов из разных мест (и вы не придумали другого способа сделать это). Подробнее о UUID: http://mysql.rjweb.org/doc.php/uuid (он включает функции для преобразования между стандартными 36-символьными UUID и BINARY (16).)

Иметь как UNIQUE AUTO_INCREMENT, так и UNIQUE UUID в одной и той же таблице - пустая трата.

Когда происходит INSERT, все уникальные / первичные ключи должны быть проверены на наличие дубликатов. Любого уникального ключа достаточно для требования InnoDB иметь ПЕРВИЧНЫЙ КЛЮЧ. BINARY (16) (16 байтов) несколько громоздко (аргумент против того, чтобы сделать его PK), но не так уж плохо. Громоздкость имеет значение, когда у вас есть вторичные ключи. InnoDB незаметно прикрепляет ПК к концу каждого вторичного ключа. Главный урок здесь - минимизировать количество вторичных ключей, особенно для очень больших таблиц. Для сравнения: INT UNSIGNED - это 4 байта с диапазоном 0..4 миллиарда. BIGINT составляет 8 байтов.

Toskan
источник
4

Если вы просто посмотрите на альтернативы, например, для простого приложения базы данных, чтобы каждый раз запрашивать базу данных перед созданием нового объекта, вы скоро обнаружите, что использование UUID может эффективно снизить сложность вашей системы. Конечно - если вы используете ключи int, это 32-битные, которые будут хранить в четверти 128-битного UUID. Конечно, алгоритмы генерации UUID требуют больше вычислительной мощности, чем простое увеличение числа. Но кого это волнует? Накладные расходы на управление «полномочиями» по назначению уникальных номеров легко перевешивают это на порядки, в зависимости от предполагаемого пространства идентификаторов уникальности.

Мирко Клемм
источник
3

На UUID == ленивый дизайн

Я не согласен с выбором твоих боев. Если дубликат UUID статистически невозможен, а математика доказана, зачем беспокоиться? Тратить время на проектирование вашей небольшой системы генерации N UUID непрактично, всегда есть дюжина других способов улучшить вашу систему.

Джонно Нолан
источник
1

На моей последней работе мы получали объекты от третьих лиц, которые были однозначно идентифицированы с помощью UUID. Я ввел таблицу поиска UUID-> long integer и использовал длинные целые числа в качестве первичных ключей, потому что так было намного быстрее.

Пол Томблин
источник
Да, конечно, третья сторона, заставляющая вас использовать UUID, - это еще одна проблема, в которую я не хочу вдаваться. Предполагая, что у вас есть возможность использовать UUID или нет.
Pyrolistical
Ну, «длинное целое число» (128 бит) на самом деле и есть UUID. Он просто показан как строка для потребления человеком. Иногда он может быть передан таким образом, но для хранения и индексации он, безусловно, будет быстрее в целочисленной форме, как вы обнаружили.
Николь
1

Использование алгоритма версии 1 кажется невозможным при ограничении, что менее 10 UUID в миллисекунду генерируются с одного и того же MAC-адреса.

Концептуально первоначальная (версия 1) схема генерации UUID заключалась в объединении версии UUID с MAC-адресом компьютера, который генерирует UUID, и с количеством 100-наносекундных интервалов с момента принятия григорианского календаря на Западе. , На практике реальный алгоритм более сложен. Эта схема подверглась критике за то, что она недостаточно «непрозрачна»; он раскрывает как личность компьютера, сгенерировавшего UUID, так и время, когда он это сделал.

Кто-нибудь поправит меня, если я неправильно понял, как это работает

Davy8
источник
Существует много версий, и многие программные системы (например, Java) не могут использовать версию 1, поскольку у нее нет чистого Java-способа доступа к MAC-адресу.
Pyrolistical
Относительно неспособности Java получить MAC-адрес: Не совсем так. Для этого есть обходные пути. Вы можете вручную установить MAC-адрес, используемый генератором, через файл конфигурации. Вы также можете вызвать ifconfig и проанализировать вывод. В написанном мною генераторе Ruby UUID используются оба подхода.
Боб Аман,
Кроме того, как упоминалось в моем ответе, если вы не можете получить MAC-адрес для UUID версии 1, вместо этого вы используете 6 случайных байтов, как указано в разделе 4.5 RFC 4122. Так что даже если вы не хотите использовать ни один из два обходных пути для Java, вы все равно можете сгенерировать действительный UUID версии 1.
Боб Аман,
GUID MS - это просто случайные числа. У них больше нет MAC-части, потому что это позволило реконструировать MAC-адрес сервера (что оказалось очень опасным).
Stefan Steiger
1

Тем, кто говорит, что UUID - это плохой дизайн, потому что они могут (с некоторой смехотворно малой вероятностью) столкнуться, в то время как ключи, сгенерированные вашей БД, не ... -предвиденная необходимость ДАЛЕКО ДАЛЕКО выше, чем вероятность столкновения UUID4. Мы знаем, что если база данных будет воссоздана, ее идентификаторы снова будут начинаться с 1, и скольким из нас приходилось воссоздавать таблицу, когда мы были уверены, что нам это никогда не понадобится? Я бы вложил свои деньги в безопасность UUID, когда в любой день что-то пойдет не так с неизвестным-неизвестным.

Иэн Дункан
источник
0

Помимо случаев, когда вам нужно использовать чужой API, который требует UUID, конечно, всегда есть другое решение. Но решат ли эти альтернативы все проблемы, с которыми сталкиваются UUID? Сможете ли вы в конечном итоге добавить еще несколько слоев хаков, каждый для решения другой проблемы, когда вы могли бы решить их все сразу?

Да, теоретически возможно столкновение UUID. Как отмечали другие, это до смешного маловероятно, что об этом просто не стоит думать. Этого никогда не было и, скорее всего, никогда не будет. Забудь об этом.

Самый «очевидный» способ избежать коллизий - позволить одному серверу генерировать уникальные идентификаторы для каждой вставки, что, очевидно, создает серьезные проблемы с производительностью и вообще не решает проблему автономной генерации. К сожалению.

Другое «очевидное» решение - это центральный орган, который заранее раздает блоки уникальных номеров, что, по сути, и делает UUID V1, используя MAC-адрес генерирующей машины (через IEEE OUI). Но дублирование MAC-адресов действительно происходит, потому что каждый центральный орган в конечном итоге облажается, поэтому на практике это гораздо более вероятно, чем конфликт UUID V4. К сожалению.

Лучший аргумент против использования UUID - то, что они «слишком велики», но (значительно) меньшая схема неизбежно не сможет решить самые интересные проблемы; Размер UUID является неотъемлемым побочным эффектом их полезности для решения этих самых проблем.

Возможно, ваша проблема недостаточно велика, чтобы требовать того, что предлагают UUID, и в этом случае не стесняйтесь использовать что-то еще. Но если ваша проблема неожиданно нарастает (а это происходит в большинстве случаев), вы в конечном итоге переключитесь на нее позже - и начнете ругать себя за то, что не использовали их вообще. Зачем проектировать на неудачу, если вместо этого просто проектировать на успех?

Stephens
источник
-10

UUID воплощают в себе все плохие методы кодирования, связанные с глобальными переменными, только хуже, поскольку они являются суперглобальными переменными, которые могут быть распределены по разным частям набора.

Недавно столкнулся с такой проблемой при замене принтера на точную заменяющую модель и обнаружил, что ни одно клиентское программное обеспечение не работает.

Джон Доу
источник
2
Рад, что мы живем в обществе, которое по-прежнему сосредотачивается на фактах, а не на случайных мнениях, иначе все мы, столкнувшись с переполнением стека, остались бы без работы. :)
Makarand