Я знаю, що рандомізовані UUID мають теоретичну дуже, дуже-дуже низьку ймовірність зіткнення, але мені цікаво на практиці, наскільки хороший Java в randomUUID()
тому, що він не має зіткнення? Хтось має досвід поділитися?
Я знаю, що рандомізовані UUID мають теоретичну дуже, дуже-дуже низьку ймовірність зіткнення, але мені цікаво на практиці, наскільки хороший Java в randomUUID()
тому, що він не має зіткнення? Хтось має досвід поділитися?
Відповіді:
Використовується UUID java.security.SecureRandom
, який повинен бути "криптографічно сильним". Хоча фактична реалізація не визначена і може змінюватись між JVM (мається на увазі, що будь-які конкретні заяви зроблені лише для одного конкретного JVM), вона наказує, що вихід повинен пройти тест статистичного генератора випадкових чисел.
Завжди можливо, що впровадження містить тонкі помилки, які все це руйнують (див. Помилку генерації ключів OpenSSH), але я не думаю, що є конкретні причини для занепокоєння щодо випадковості Java UUID.
У Вікіпедії є дуже хороша відповідь http://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions
кількість випадкових UUID версії 4, які необхідно генерувати для того, щоб мати 50% -ву ймовірність принаймні одного зіткнення, становить 2,71 квінтільйона, обчислюється так:
...
Це число еквівалентно генеруванню 1 мільярда UUID в секунду протягом приблизно 85 років, а файл, що містить це багато UUID, у 16 байт на UUID, буде приблизно 45 екбабайтів, у багато разів більший, ніж найбільші бази даних, що існують зараз порядку сотень петабайт.
...
Таким чином, щоб не було шансів на дублювання одного мільярда, необхідно створити 103 трильйони UUID версії 4.
UUID.randomUUID()
, а не в теоретичних шансах для даного ідеального генератора випадкових чисел.
Хтось має досвід поділитися?
Існують 2^122
можливі значення для UUID типу-4. (Специфікація говорить про те, що ви втрачаєте 2 біти для типу та ще 4 біти для номера версії.)
Якщо припустити, що ви повинні генерувати 1 мільйон випадкових UUID в секунду, шанси появи дубліката у вашому житті були б малі малі. А щоб виявити дублікат, вам доведеться вирішити проблему порівняння 1 мільйона нових UUID в секунду проти всіх UUID, які ви раніше генерували 1 !
Шанси, що хтось відчув (тобто насправді помітив ) дублікат у реальному житті, навіть менші, ніж зникаючі малі ... через практичні труднощі пошуку зіткнень.
Звичайно, ви, як правило, використовуєте генератор псевдовипадкових чисел, а не джерело справді випадкових чисел. Але я думаю, ми можемо бути впевнені, що якщо ви використовуєте надійного постачальника для своїх криптографічних силових випадкових чисел, то це буде криптографічна сила, а ймовірність повторів буде такою ж, як і для ідеального (не упередженого) генератора випадкових чисел .
Однак якщо ви користувались JVM із "зламаним" генератором крипто-випадкових чисел, усі ставки виключаються. (І це може включати деякі шляхи вирішення проблем "дефіциту ентропії" в деяких системах. Або можливість того, що хтось пограбував із вашим JRE, або у вашій системі, або вище за течією.)
1 - Якщо припустити, що ви використовували "якусь бінарну btree", запропоновану анонімним коментатором, кожному UUID знадобляться O(NlogN)
біти оперативної пам'яті, щоб представляти N
різні UUID, припускаючи низьку щільність і випадковий розподіл бітів. Тепер помножте це на 1 000 000 і кількість секунд, для яких ви збираєтеся запустити експеримент. Я не думаю, що це практично для тривалості часу, необхідного для тестування на зіткнення високоякісної РНГ. Навіть з (гіпотетичними) розумними уявленнями.
Я не фахівець, але припускаю, що достатньо розумних людей за ці роки дивився генератор випадкових чисел Java. Отже, я також припускаю, що випадкові UUID є хорошими. Таким чином, ви дійсно повинні мати теоретичну ймовірність зіткнення (що становить приблизно 1: 3 × 10 ^ 38 для всіх можливих UUID. Хто-небудь знає, як це змінюється лише для випадкових UUID? Це 1/(16*4)
вищезазначене?)
Зі свого практичного досвіду я досі не бачив жодних зіткнень. Я, мабуть, виростив приголомшливо довгу бороду того дня, коли отримаю свою першу;)
У колишнього роботодавця ми мали унікальну колонку, яка містила випадковий uuid. У нас відбулося зіткнення перший тиждень після його розгортання. Звичайно, шанси низькі, але вони не нульові. Ось чому Log4j 2 містить UuidUtil.getTimeBasedUuid. Він генерує UUID, який є унікальним протягом 8 925 років, якщо ви не генеруєте більше 10 000 UUID / мілісекунд на одному сервері.
Початкова схема покоління для UUID полягала у поєднанні версії UUID з MAC-адресою комп'ютера, що генерує UUID, та з кількістю інтервалів 100 наносекунд з моменту прийняття григоріанського календаря на Заході. Представляючи єдину точку в просторі (комп'ютер) і часу (кількість інтервалів), шанс зіткнення значень фактично нульовий.
Багато відповідей обговорюють, скільки UUID повинні створити для досягнення 50% шансу зіткнення. Але 50%, 25%, а то й 1% шанс зіткнення марний для програми, коли зіткнення має бути (практично) неможливим.
Чи програмісти звичайно відкидають як "неможливі" інші події, які можуть і можуть відбутися?
Коли ми записуємо дані на диск або пам'ять і читаємо їх знову, ми вважаємо належними дані, що вони є правильними. Ми розраховуємо на виправлення помилок пристрою, щоб виявити будь-які пошкодження. Але ймовірність виявлення помилок насправді близько 2 -50 .
Чи не було б сенсу застосовувати подібний стандарт до випадкових UUID? Якщо ви це зробите, ви виявите, що "неможливе" зіткнення можливе в колекції близько 100 мільярдів випадкових UUID (2 36,5 ).
Це астрономічне число, але такі програми, як деталізовані рахунки в національній системі охорони здоров’я або введення даних високочастотних датчиків на великий масив пристроїв, безумовно, можуть натрапити на ці межі. Якщо ви пишете наступне керівництво автостопом до Галактики, не намагайтеся призначити 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 мільйона записів. Отже, хоча ця функція хороша, з точки зору того, що не виникає зіткнень, для мене це не здається таким хорошим, як це було б теоретично.
Редагувати :
Дуже багато людей, здається, не розуміють цієї відповіді, тому я поясню свою думку: я знаю, що подібність "мала" і далеко не повне зіткнення. Однак я просто хотів порівняти Java UUID.randomUUID () з справжнім генератором випадкових чисел, що є актуальним питанням.
У справжньому генераторі випадкових чисел вірогідність останнього випадку складе приблизно 0,007%. Тому я думаю, що мій висновок стоїть.
Формула пояснюється в цій статті wiki en.wikipedia.org/wiki/Birthday_problem
Я грав на лотереї минулого року, і ніколи не вигравав .... але, схоже, там лотерея має переможців ...
doc: http://tools.ietf.org/html/rfc4122
Тип 1: не реалізовано. зіткнення можливе, якщо uuid генерується в той же момент. impl можна штучно синхронізувати, щоб обійти цю проблему.
Тип 2: ніколи не бачити реалізацію.
Тип 3: хеш md5: можливе зіткнення (128 біт-2 технічні байти)
Тип 4: випадковий: можливе зіткнення (як лотерея). зауважте, що jdk6 impl не використовує "справжній" захищений випадковий випадок, оскільки алгоритм PRNG не вибирається розробником, і ви можете змусити систему використовувати "поганий" PRNG algo. Тож ваш UUID передбачуваний.
Тип 5: хеш sha1: не реалізовано: можливе зіткнення (160 біт-2 технічні байти)