Как сказано в комментариях, если вы получаете коллизии с UUID только с миллионом строк (да, просто), это потому, что ваша генерация случайных uuid использует какой-то слабый генератор случайных чисел.
Поскольку вы сейчас опубликовали свой uuid_v4()
функция, и она опирается на MySQL rand()
, я могу объяснить, почему ваш код не работает.
Согласно mysql документы: http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html
RAND () не должен быть идеальным генератором случайных чисел. Это быстрый способ генерирования случайных чисел по требованию, переносимый между платформами для одной и той же MySQL версии.
Это означает, что вы не можете использовать mysql для генерации uuid, в по крайней мере, не с RAND()
функцией.
Вам нужно будет сгенерировать uuid за пределами mysql из возможных. Для этого есть несколько библиотек на многих языках:
Всегда проверяйте, использует ли выбранная вами библиотека криптографически безопасный генератор случайных чисел.
ОБНОВЛЕНИЕ
Можно генерировать безопасный UUID V4 на стороне MySQL, используя функцию random_bytes()
:
Эта функция возвращает двоичную строку случайных байтов len генерируется с использованием генератора случайных чисел из библиотеки SSL.
Таким образом, мы можем обновить вашу функцию следующим образом:
CREATE FUNCTION uuid_v4s()
RETURNS CHAR(36)
BEGIN
-- 1th and 2nd block are made of 6 random bytes
SET @h1 = HEX(RANDOM_BYTES(4));
SET @h2 = HEX(RANDOM_BYTES(2));
-- 3th block will start with a 4 indicating the version, remaining is random
SET @h3 = SUBSTR(HEX(RANDOM_BYTES(2)), 2, 3);
-- 4th block first nibble can only be 8, 9 A or B, remaining is random
SET @h4 = CONCAT(HEX(FLOOR(ASCII(RANDOM_BYTES(1)) / 64)+8),
SUBSTR(HEX(RANDOM_BYTES(2)), 2, 3));
-- 5th block is made of 6 random bytes
SET @h5 = HEX(RANDOM_BYTES(6));
-- Build the complete UUID
RETURN LOWER(CONCAT(
@h1, '-', @h2, '-4', @h3, '-', @h4, '-', @h5
));
END
Эта функция должна быть достаточно безопасной для использования, не заботясь о коллизиях, если только у вас очень большое количество строк.
Test
Я создал следующий сценарий тестирования: вставьте случайный UUID v4 в качестве первичного ключа для таблицы, пока не будет создано 40 000 000 строк. При обнаружении коллизии строка обновляется с шагом collisions
столбец:
INSERT INTO test (uuid) VALUES (uuid_v4()) ON DUPLICATE KEY UPDATE collisions=collisions+1;
Сумма коллизий после 40 миллионов строк с каждой функцией:
+----------+----------------+
| RAND() | RANDOM_BYTES() |
+----------+----------------+
| 55 | 0 |
+----------+----------------+
Число коллизий в обоих сценариях ios имеет тенденцию к увеличению с ростом числа строк.