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


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

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

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

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

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

9 67

9 ответов:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

КОЛЛЕКТОР = УНИКАЛЬНЫЙ ПРИСВОЕН МАШИНЕ

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • выберите другой тип индекса (например, некластеризованный на MSSQL), который не возражает против этого
  • портит данные, чтобы переместить энтропии низкого порядка битов (например, изменение порядка байтов В1 идентификаторы UUID на MySQL)
  • сделать Идентификатор вторичного ключа с автоинкрементным первичным ключом инт

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

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