У меня есть требование к проекту, где
Мы ожидаем, что многие миллионы записей будут добавляться в базу данных каждый месяц.
Я попробовал решения здесь: PHP: Как создать случайную уникальную буквенно-цифровую строку?
и хотя они, кажется, сначала работают, мое тестирование показало, что со временем появятся дубликаты.
Сейчас я смотрю на использование uniqid с префиксом. Проблема, которую я обнаружил при использовании uniqid без префикса, заключается в том, что дубликаты будут генерироваться, когда одновременные запросы поступают на сервер в одно и то же время. Я надеюсь, что использование префикса решит эту проблему.
Я думаю об использовании этой функции:
private function generate_id()
{
$alpha_numeric = 'ABCDEFGHIJKLMNPQRSTUVWXYZ0123456789';
$max = strlen($alpha_numeric);
$prefix = '';
for ($i = 0; $i < 5; $i++)
{
$prefix .= $alpha_numeric[random_int(0, $max - 1)];
}
return strtoupper(uniqid($prefix));
}
Префиксом будет 5-буквенная буквенно-цифровая строка. Будет ли этого достаточно, чтобы удовлетворить мои требования?
*****Редактировать*****
Использование предложенного UUID было бы лучшим способом ограничения вероятности столкновения, но было решено использовать описанный выше подход, но увеличить префикс до 7 символов. Вероятность столкновения, если два идентификатора генерируются в одну и ту же миллисекунду, будет около 1 на 8,3 миллиона. Это было сочтено приемлемым для более высоких взлетов.
Если вы используете Composer или внешние библиотеки, смотрите https://github.com/ramsey/uuid
или эта функция может удовлетворить ваши потребности. Для ваших нужд strtoupper
результат:
/**
* generate
*
* Returns a version 4 UUID
*
* @access public
* @return string
*/
public static function generate()
{
$data = openssl_random_pseudo_bytes(16);
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100
$data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
Увидеть https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
Рассматривали ли вы использование уникального ключа в базе данных для обеспечения уникальности? В этом случае вам не придется самостоятельно проверять наличие дубликатов, но вы сгенерируете уникальное значение и попытаетесь вставить запись в БД, пока не добьетесь успеха.
Если MySQL, то прочитайте это — Использование уникального индекса MySQL для предотвращения дубликатов. Если нет — посмотрите документацию вашей базы данных по вашему выбору.
uniquid не гарантирует уникальность возвращаемого значения! Используйте функцию с more_entropy, установленным в TRUE, чтобы увеличить шансы уникального значения.
return strtoupper(uniqid($prefix), true);
Нужно ли ограничивать себя только заглавными буквами и цифрами? Это уменьшит максимальное количество уникальных значений, генерируемых функцией, в отличие от использования прописных, строчных букв, цифр и символов.
Вы также можете рассмотреть криптографические функции для увеличения случайности.
Если вы используете PHP7, посмотрите на http://php.net/manual/en/function.random-bytes.php
например
<?php
echo strtoupper(bin2hex(random_bytes(32)));
?>
Должно быть достаточно уникальным для ваших требований, используйте больше байтов, если считаете нужным.
Вообще говоря — всегда будут возможные дубликаты, когда вы не можете проверить в базе данных существующие значения. Все, что вы можете сделать, это уменьшить вероятность того, что дубликаты будут достаточно низкими для вашего варианта использования. Это идея позади GUID.
Если вы действительно не можете получить доступ к базе данных и если вы действительно ограничены заглавными буквами, я бы порекомендовал генерировать GUID с uniqid функция, затем удаляя ненужные символы и переводя их в верхний регистр. Если вы боитесь, что могут возникнуть дубликаты, объедините два или более идентификатора GUID, чтобы уменьшить эту вероятность.
Что-то вроде:
$unique_string = str_replace(".", "", strtoupper(uniqid(uniqid(uniqid(), true), true)));