Производительность UUID в MySQL? - PullRequest
72 голосов
/ 02 марта 2010

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

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

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

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

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

Ответы [ 9 ]

71 голосов
/ 28 сентября 2011

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

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

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

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

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

Таким образом, процесс слияния возможен (UUID будет вашим РЕАЛЬНЫМ ключом, в то время как PK будет просто временным, что даст вам хорошую производительность).

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

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

31 голосов
/ 02 марта 2010

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

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

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

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

25 голосов
/ 02 марта 2010

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

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

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

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

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

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

5 голосов
/ 16 октября 2012

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

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

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

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

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

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

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

3 голосов
/ 13 декабря 2014

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

Сервер ключей поддерживает следующий доступный идентификатор

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

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

2 голосов
/ 15 февраля 2011

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

1 голос
/ 03 марта 2010

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

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

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

1 голос
/ 02 марта 2010

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

0 голосов
/ 01 августа 2018

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

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

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

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

...