Производительность UUID в MySQL?

мы рассматриваем использование значений UUID в качестве первичных ключей для нашей базы данных MySQL. Вставляемые данные генерируются с десятков, сотен или даже тысяч удаленных компьютеров и вставляются со скоростью 100-40 000 вставок в секунду, и мы никогда не будем делать никаких обновлений.

сама база данных обычно получает около 50M записей, прежде чем мы начнем отбирать данные, поэтому не массивная база данных, но и не крошечная. Мы также планируем работать на InnoDB, хотя мы открыты для изменить это, если есть лучший двигатель для того, что мы делаем.

мы были готовы пойти с UUID типа 4 Java, но в тестировании наблюдалось некоторое странное поведение. Во - первых, мы храним как varchar(36), и теперь я понимаю, что нам лучше использовать binary(16) - хотя насколько лучше, я не уверен.

больший вопрос: насколько сильно эти случайные данные искажают индекс, когда у нас есть записи 50M? Было бы лучше, если бы мы использовали, например, UUID типа 1 где битах были сохраняться? Или, может быть, мы должны полностью отказаться от UUID и рассмотреть первичные ключи auto_increment?

Я ищу общие мысли/советы по производительности различных типов UUID, когда они хранятся в качестве индекса / первичного ключа в MySQL. Спасибо!

9 ответов


UUID-это универсальный уникальный идентификатор. Это универсальная часть, которую вы должны рассмотреть здесь.

вы действительно нужны коды для универсального уникального? Если это так, то UUIDs может быть вашим единственным выбором.

Я настоятельно рекомендую, если вы do используйте UUIDs, вы храните их как число, а не как строку. Если у вас есть записи 50M+, то экономия места для хранения улучшит вашу производительность (хотя я не могу сказать, как гораздо.)

Если ваши идентификаторы не должны быть универсально уникальными, то я не думаю, что вы можете сделать намного лучше, чем просто использовать auto_increment, который гарантирует, что идентификаторы будут уникальными в таблице (так как значение будет увеличиваться каждый раз)


на моей работе мы используем UUID как PKs. Что я могу сказать вам по опыту, это не использовать их как PKs (SQL Server, кстати).

Это одна из тех вещей, которые, когда у вас меньше 1000 записей;S хорошо, но когда у вас есть миллионы, это худшее, что вы можете сделать. Почему? Поскольку UUID не являются последовательными, поэтому каждый раз, когда новая запись вставляется, MSSQL должен посмотреть правильную страницу, чтобы вставить запись, а затем вставить запись. Действительно уродливые последствия с этим что страницы в конечном итоге все в разных размерах, и они в конечном итоге фрагментированы, поэтому теперь мы должны делать периодическую де-фрагментацию.

когда вы используете автоинкремент, MSSQL всегда будет идти на последнюю страницу, и вы получите страницы одинакового размера (теоретически), поэтому производительность для выбора этих записей намного лучше (также потому, что вставки не будут блокировать таблицу/страницу так долго).

однако большим преимуществом использования UUID в качестве PKs является то, что если у нас есть кластеры DBs, там не будет конфликтов при слиянии.

Я бы рекомендовал следующую модель: 1. ПК ИНТ тож 2. Дополнительный столбец автоматически генерируется как UUID.

таким образом, процесс слияния возможен (UUID будет вашим реальным ключом, в то время как PK будет просто чем-то временным, что дает вам хорошую производительность).

Примечание: лучшее решение-использовать NEWSEQUENTIALID (как я говорил в комментариях), но для устаревшего приложения с небольшим временем для рефакторинга (и хуже того, не контролируя все вставки), это невозможно сделать. Но действительно, с 2017 года я бы сказал, что лучшим решением здесь является NEWSEQUENTIALID или doing Guid.Расческа с NHibernate.

надеюсь, что это помогает


следует учитывать, что Автоинкременты генерируются по одному за раз и не могут быть решены с помощью параллельного решения. Борьба за использование UUIDs в конечном итоге сводится к тому, чего вы хотите достичь, а не к тому, чем вы потенциально жертвуете.

на производительность, коротко:

UUID, как и выше, составляет 36 символов, включая дефисы. Если вы храните этот VARCHAR (36), вы снижение производительности сравнения драматический. Это ваш первичный кей, ты же не хочешь, чтобы это было медленно.

на своем битовом уровне UUID составляет 128 бит, это означает, что он будет вписываться в 16 байт, обратите внимание, что это не очень читабельно, но он будет держать хранение низким, и только в 4 раза больше, чем 32-битный int, или в 2 раза больше, чем 64-битный int. Я буду использовать VARBINARY (16) Теоретически, это может работать без много накладных расходов.

Я рекомендую прочитать следующие два сообщения:

Я считаю, между двумя, они отвечают на ваш вопрос.


Я склонен избегать UUID просто потому, что это боль для хранения и боль для использования в качестве первичного ключа, но есть преимущества. Главное - они уникальны.

Я обычно решаю проблему и избегаю UUID, используя двойные ключевые поля.

COLLECTOR = УНИКАЛЬНЫЙ, НАЗНАЧЕННЫЙ МАШИНЕ

ID = запись, собранная коллектором (поле auto_inc)

Это дает мне две вещи. Скорость полей auto-inc и уникальность данных, хранящихся в Центральном расположение после того, как он собран и сгруппирован вместе. Я также знаю, просматривая данные, где они были собраны, что часто очень важно для моих нужд.

Я видел много случаев, имея дело с другими наборами данных для клиентов, где они решили использовать UUID, но затем все еще имеют поле для сбора данных, что действительно является пустой тратой усилий. Просто использование двух (или более, если необходимо) полей в качестве ключа действительно помогает.

Я только что видел слишком много хиты производительности с использованием UUID. Они чувствуют себя обманщиками...


вместо централизованного создания уникальных ключей для каждой вставки, как насчет выделения блоков ключей отдельным серверам? Когда у них закончатся ключи, они могут запросить новый блок. Затем вы решаете проблему накладных расходов, подключаясь для каждой вставки.

Keyserver поддерживает следующий доступный id

  • сервер 1 запрашивает блок id.
  • сервер возвращает (1,1000)
    сервер 1 может вставлять 1000 записей, пока не потребуется запросить новый блок
  • сервер 2 запрашивает индексный блок.
  • сервер ключей возвращает (1001,2000)
  • etc...

вы можете придумать более сложную версию, где сервер может запросить количество необходимых ключей или вернуть неиспользуемые блоки на сервер ключей, который, конечно, должен будет поддерживать карту используемых/неиспользуемых блоков.


Я бы назначил каждому серверу числовой идентификатор транзакционным способом. Затем каждая вставленная запись будет просто автоматически создавать свой собственный счетчик. Комбинация ServerID и RecordID будет уникальной. Поле ServerID можно индексировать и в будущем выбрать производительность на основе ServerID (при необходимости) может быть намного лучше.


Как насчет некоторых uid ручной работы? Дайте каждому из тысяч серверов идентификатор и сделайте первичный ключ комбинированным ключом автоинкремента, MachineID ???


поскольку первичный ключ генерируется децентрализованным, у вас нет возможности использовать auto_increment в любом случае.

Если вам не нужно скрывать идентификатор удаленных машин, используйте UUID типа 1 вместо UUID. Они легче генерировать и, по крайней мере, не могут повредить производительности базы данных.

то же самое касается varchar (char, действительно) против binary: это может только помочь. Действительно ли важно, насколько улучшена производительность?


короткий ответ заключается в том, что многие базы данных имеют проблемы с производительностью (в частности, с высокими томами вставки) из-за конфликта между их методом индексирования и преднамеренной энтропией UUID в битах высокого порядка. Существует несколько распространенных хаков:

  • выберите другой тип индекса (например, некластеризованный на MSSQL), который не возражает против этого
  • munge данные для перемещения энтропии в биты нижнего порядка (например, переупорядочение байтов V1 UUIDs на MySQL)
  • сделать UUID вторичный ключ с автоматическим приращением int первичный ключ

... но это все хаки-и, вероятно, хрупкие.

лучший ответ, но, к сожалению, самый медленный, - потребовать от вашего поставщика улучшить свой продукт, чтобы он мог иметь дело с UUIDs в качестве первичных ключей, как и любой другой тип. Они не должны заставлять вас сворачивать свой собственный наполовину испеченный хак, чтобы компенсировать их неспособность решить то, что стало общим случаем использования, и будет только продолжать расти.