Вероятность коллизии при использовании наиболее значимых битов UUID в Java - PullRequest
231 голосов
/ 28 ноября 2008

Если я использую Long uuid = UUID.randomUUID().getMostSignificantBits(), какова вероятность столкновения. Он отрезает наименее значимые биты, поэтому есть вероятность, что вы столкнетесь с коллизией, верно?

Ответы [ 5 ]

211 голосов
/ 28 ноября 2008

Согласно документации , статический метод UUID.randomUUID() генерирует UUID типа 4.

Это означает, что шесть битов используются для некоторой информации о типе, а оставшиеся 122 бита назначаются случайным образом.

Шесть неслучайных битов распределяются с четырьмя в наиболее значимой половине UUID и двумя в наименее значимой половине. Таким образом, наиболее значимая половина вашего UUID содержит 60 бит случайности, что означает, что вам в среднем нужно сгенерировать 2 ^ 30 UUID для получения коллизии (по сравнению с 2 ^ 61 для полного UUID).

Так что я бы сказал, что вы в безопасности. Однако обратите внимание, что это совершенно не так для других типов UUID, как упоминает Карл Селеборг.

Кстати, вам будет немного лучше, если использовать наименее значимую половину UUID (или просто сгенерировать случайный длинный с использованием SecureRandom).

55 голосов
/ 28 ноября 2008

У Раймонда Чена есть отличная запись в блоге на эту тему:

GUID глобально уникальны, но подстроки GUID не

13 голосов
/ 15 мая 2012

Я думаю, что это лучший пример для использования randomUUID:

http://www.javapractices.com/topic/TopicAction.do?Id=56

10 голосов
/ 14 марта 2009

Вам лучше просто генерировать случайное длинное значение, тогда все биты являются случайными. В Java 6 новый метод Random () использует System.nanoTime () плюс счетчик в качестве начального числа.

Существуют разные уровни уникальности.

Если вам нужна уникальность на многих машинах, у вас может быть центральная таблица базы данных для распределения уникальных идентификаторов или даже пакетов уникальных идентификаторов.

Если вам просто нужно иметь уникальность в одном приложении, вы можете просто иметь счетчик (или счетчик, который начинается с currentTimeMillis () * 1000 или nanoTime () в зависимости от ваших требований)

7 голосов
/ 03 мая 2013

Используйте время YYYYDDDD (год + день года) в качестве префикса. Это уменьшает фрагментацию базы данных в таблицах и индексах. Этот метод возвращает byte[40]. Я использовал его в гибридной среде, где SID Active Directory (varbinary(85)) является ключом для пользователей LDAP, а автоматически созданный идентификатор приложения используется для пользователей, не являющихся LDAP. Также большое количество транзакций в день в таблицах транзакций (Банковская индустрия) не может использовать стандартные типы Int для ключей

private static final DecimalFormat timeFormat4 = new DecimalFormat("0000;0000");

public static byte[] getSidWithCalendar() {
    Calendar cal = Calendar.getInstance();
    String val = String.valueOf(cal.get(Calendar.YEAR));
    val += timeFormat4.format(cal.get(Calendar.DAY_OF_YEAR));
    val += UUID.randomUUID().toString().replaceAll("-", "");
    return val.getBytes();
}
...