Мы рассматриваем возможность использования значений UUID в качестве первичных ключей для нашей базы данных MySQL. Вставляемые данные генерируются с десятков, сотен или даже тысяч удаленных компьютеров и вставляются со скоростью 100-40 000 вставок в секунду, и мы никогда не будем делать никаких обновлений.
Сама база данных обычно достигает около 50 миллионов записей, прежде чем мы начнем отбирать данные, так что это не массивная база данных, но и не маленькая. Мы также планируем работать на InnoDB, хотя мы готовы изменить это, если есть лучший движок для того, что мы делаем.
Мы были готовы использовать UUID Java Type 4, но во время тестирования наблюдали странное поведение. Во-первых, мы сохраняем как varchar (36), и теперь я понимаю, что нам было бы лучше использовать binary (16) - хотя насколько лучше, я не уверен.
Более важный вопрос: насколько сильно эти случайные данные портят индекс, когда у нас есть 50 миллионов записей? Было бы лучше, если бы мы использовали, например, UUID типа 1, где крайние левые биты были отмечены временем? Или, может быть, нам следует полностью отказаться от UUID и рассмотреть первичные ключи auto_increment?
Я ищу общие мысли / советы по производительности различных типов UUID, когда они хранятся как индекс / первичный ключ в MySQL. Благодаря!
Ответы:
UUID - это универсальный уникальный идентификатор. Это универсальная часть, которую вы должны здесь учитывать.
Вам действительно нужно, чтобы идентификаторы были универсально уникальными? Если так, то UUID может быть вашим единственным выбором.
Я настоятельно рекомендую, если вы действительно используете UUID, вы храните их как числа, а не как строку. Если у вас более 50 миллионов записей, то экономия места для хранения улучшит вашу производительность (хотя я не могу сказать насколько).
Если ваши идентификаторы не должны быть универсально уникальными, я не думаю, что вы можете добиться большего, чем просто использовать auto_increment, который гарантирует, что идентификаторы будут уникальными в таблице (поскольку значение будет увеличиваться каждый раз)
источник
binary
формат. Я имею в виду 128-битное число, а не 288-битную строку. Например, слово «привет» в ASCII -68 65 6C 6C 6F
это число 448 378 203 247. Для хранения строки «68656C6C6F» требуется 10 байтов. Для числа 448 378 203 247 требуется только 5. В общем, если вам действительно не нужен первый U в UUID, вы не сможете добиться большего, чемauto_increment
На моей работе мы используем UUID как PK. По опыту могу сказать, что НЕ ИСПОЛЬЗУЙТЕ ИХ как ПК (кстати, SQL Server).
Это одна из тех вещей, когда у вас меньше 1000 записей, это нормально, но когда у вас миллионы, это худшее, что вы можете сделать. Почему? Поскольку UUID не являются последовательными, поэтому каждый раз, когда вставляется новая запись, MSSQL должен переходить на правильную страницу для вставки записи, а затем вставлять запись. Действительно неприятным последствием этого является то, что все страницы оказываются разного размера и оказываются фрагментированными, поэтому теперь мы должны периодически выполнять дефрагментацию.
Когда вы используете автоинкремент, MSSQL всегда будет переходить на последнюю страницу, и вы получите страницы одинакового размера (теоретически), поэтому производительность для выбора этих записей намного выше (также потому, что INSERT не будут блокировать таблицу / страницу для так долго).
Однако большим преимуществом использования UUID в качестве PK является то, что если у нас есть кластеры БД, при слиянии не будет конфликтов.
Я бы порекомендовал следующую модель: 1. PK INT Identity 2. Дополнительный столбец автоматически генерируется как UUID.
Таким образом, возможен процесс слияния (UUID будет вашим НАСТОЯЩИМ ключом, а PK будет чем-то временным, что дает вам хорошую производительность).
ПРИМЕЧАНИЕ. Лучшее решение - использовать NEWSEQUENTIALID (как я уже говорил в комментариях), но для устаревшего приложения, у которого мало времени на рефакторинг (и, что еще хуже, не контролирует все вставки), это невозможно сделать. Но на самом деле, по состоянию на 2017 год, я бы сказал, что лучшим решением здесь является NEWSEQUENTIALID или выполнение Guid.Comb с NHibernate.
Надеюсь это поможет
источник
Следует учитывать, что автоинкременты генерируются по одному и не могут быть решены с помощью параллельного решения. Борьба за использование UUID в конечном итоге сводится к тому, чего вы хотите достичь, а не к тому, чем вы потенциально жертвуете.
На производительность, кратко :
Я рекомендую прочитать следующие два сообщения:
Я считаю, что они оба отвечают на ваш вопрос.
источник
Я стараюсь избегать UUID просто потому, что его сложно хранить и использовать в качестве первичного ключа, но есть преимущества. Главное - они УНИКАЛЬНЫЕ.
Обычно я решаю проблему и избегаю UUID, используя поля с двумя ключами.
КОЛЛЕКТОР = УНИКАЛЬНЫЙ НАЗНАЧЕН ДЛЯ МАШИНЫ
ID = ЗАПИСЬ, СОБРАННАЯ КОЛЛЕКТОРОМ (поле auto_inc)
Это предлагает мне две вещи. Скорость автоматического добавления полей и уникальность данных, хранящихся в централизованном месте после их сбора и группировки. Я также знаю, просматривая данные, где они были собраны, что часто очень важно для моих нужд.
Я видел много случаев, когда имел дело с другими наборами данных для клиентов, когда они решили использовать UUID, но при этом все еще оставалось поле, в котором были собраны данные, что на самом деле является пустой тратой усилий. Простое использование двух (или более, если необходимо) полей в качестве ключа действительно помогает.
Я только что видел слишком много падений производительности при использовании UUID. Они чувствуют себя обманщиком ...
источник
Вместо того, чтобы централизованно генерировать уникальные ключи для каждой вставки, как насчет распределения блоков ключей для отдельных серверов? Когда у них заканчиваются ключи, они могут запросить новый блок. Затем вы решаете проблему накладных расходов, подключая каждую вставку.
Сервер ключей поддерживает следующий доступный идентификатор
Сервер 1 может вставить 1000 записей, пока ему не потребуется запросить новый блок.
Вы можете придумать более сложную версию, в которой сервер мог бы запрашивать количество необходимых ключей или возвращать неиспользуемые блоки на сервер ключей, который, конечно же, должен был бы поддерживать карту использованных / неиспользуемых блоков.
источник
Я бы назначил каждому серверу числовой идентификатор транзакционным способом. Затем каждая вставленная запись будет просто автоматически увеличивать свой собственный счетчик. Комбинация ServerID и RecordID будет уникальной. Поле ServerID можно проиндексировать, и будущая производительность выбора на основе ServerID (при необходимости) может быть намного лучше.
источник
Короткий ответ заключается в том, что у многих баз данных есть проблемы с производительностью (в частности, с большими объемами INSERT) из-за конфликта между их методом индексирования и преднамеренной энтропией UUID в старших битах. Есть несколько распространенных хаков:
... но это все хаки - и, наверное, хрупкие.
Лучший ответ, но, к сожалению, самый медленный, - это потребовать от вашего поставщика улучшения продукта, чтобы он мог работать с UUID в качестве первичных ключей, как и любой другой тип. Они не должны заставлять вас использовать свой собственный недоделанный хак, чтобы компенсировать их неспособность решить то, что стало обычным вариантом использования и будет только расти.
источник
А как насчет какого-нибудь UID, созданного вручную? Дайте каждому из тысяч серверов идентификатор и сделайте первичный ключ комбинированным ключом автоинкремента, MachineID ???
источник
Поскольку первичный ключ генерируется децентрализованно, у вас все равно нет возможности использовать auto_increment.
Если вам не нужно скрывать идентификационные данные удаленных машин, используйте UUID типа 1 вместо UUID. Их легче создать, и они, по крайней мере, не могут повлиять на производительность базы данных.
То же самое касается varchar (char, на самом деле) и двоичного: это может только помочь. Неужели важно, насколько улучшена производительность?
источник
Я понимаю, что этот вопрос довольно старый, но я нашел его в своем исследовании. С тех пор произошло несколько вещей (SSD повсеместно используются, InnoDB получил обновления и т. Д.).
В своем исследовании я нашел этот довольно интересный пост о производительности:
утверждая, что из-за случайности индексов GUID / UUID деревья могут стать довольно несбалансированными. в базе знаний MariaDB я нашел еще одно сообщение, предлагающее решение. Но с тех пор как новый UUID_TO_BIN об этом позаботится . Эта функция доступна только в MySQL (протестированная версия 8.0.18), но не в MariaDB (версия 10.4.10).
TL; DR: сохранить UUID как преобразованные / оптимизированные значения BINARY (16).
источник