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

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

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

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

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

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

Ответ 1

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

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

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

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

Ответ 2

В моей работе мы используем UUID как PK. То, что я могу сказать вам по опыту, НЕ ИСПОЛЬЗУЕТ ИМ как ПК (кстати, SQL Server).

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

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

Однако большое преимущество использования UUID как PK заключается в том, что если у нас есть кластеры БД, конфликтов при слиянии не будет.

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

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

ПРИМЕЧАНИЕ. Лучшим решением является использование NEWSEQUENTIALID (как я уже говорил в комментариях), но для устаревшего приложения с небольшим количеством времени для рефакторинга (и, что еще хуже, без управления всеми вставками), это невозможно сделать, Но действительно, с 2017 года, я бы сказал, что лучшим решением здесь является NEWSEQUENTIALID или выполнение Guid.Comb с NHibernate.

Надеюсь, что это поможет

Ответ 3

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

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

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

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

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

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

Ответ 4

Я стараюсь избегать UUID просто потому, что это боль для хранения и боль для использования в качестве первичного ключа, но есть преимущества. Главное - они УНИКАЛЬНЫЕ.

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

КОЛЛЕКТОР = УНИКАЛЬНЫЙ НАЗНАЧЕН ДЛЯ МАШИНЫ

ID = ЗАПИСЬ СОБИРАЕТСЯ КОЛЛЕКТОРОМ (поле auto_inc)

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

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

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

Ответ 5

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

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

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

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

Ответ 6

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

Ответ 7

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

Ответ 8

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

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

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