2010-03-02 13 views
64

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

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

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

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

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

+0

отсутствует одна важная деталь: являются ли первичные ключи генерируемыми сервером регистрации или самими клиентскими машинами? – hop

+0

@hop они генерируются 10-1000 клиентами, которые вставляют данные –

+0

. Где вам нужна универсальная уникальность в вашем сценарии? Мой совет - придерживаться auto_increment и использовать отдельное поле для описания удаленного компьютера, который отправляет данные. Не нужно изобретать велосипед здесь. –

ответ

28

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

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

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

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

+0

Наша причина для рассмотрения UUID заключается в том, что в некоторых ситуациях у нас будет более 1000 машин, которые будут демпинговать данные, и я не хотел, чтобы мы блокировались при генерации центрального ID - хотя, возможно, я pennywise, фунт неразумный :) –

+2

Интересная точка ; это будет распараллелить генерацию ключей. Я считаю, что это увеличит производительность генерации ключей. Однако вы выбираете производительность INSERT по производительности SELECT, если вы используете VARCHAR для хранения UUID. Вы, безусловно, должны выбрать VARBINARY для хранения, чтобы обеспечить производительность SELECT.Дополнительный шаг * может * влиять на производительность INSERT, но вам будет выплачено улучшение производительности SELECT. – Dancrumb

+12

Мы закончили тем, что проводили бенчмаркинг по реальным данным, а GUID без клавиш были довольно быстрыми, GUID с ключами были ужасными (даже при сохранении как BINARY), а int w/AUTO_COMPLETE был самым быстрым. Я думаю, что в нашем случае мы действительно отсутствовали в лесу с деревьев, так как генерация последовательности казалась несущественной по сравнению со стоимостью хранения большего количества данных + с действительно дрянной BTREE из-за случайности GUID –

22

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

В исполнении, briefly:

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

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

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

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

+2

На самом деле, я прочитал обе эти статьи до публикации этого вопроса, и у меня все еще не было хорошего ответа. Например, ни один из них не говорит о типе 1 и типе 4 UUIDS :( –

+0

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

+0

@Patrick: вы вкладываете слишком много разных тем в свой вопрос – hop

1

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

+0

Я подумал об этом и, возможно, потребуется запустить некоторые тесты. Даже временная локальная последовательность на каждой из 1000 машин в сочетании с временной меткой может быть достаточно. Пример: machine_id + temp_seq + timestamp –

+0

Возможно ли иметь temp_sequence, который сбрасывает каждую отметку времени? Я не уверен. – MindStalker

1

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

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

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

2

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

57

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

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

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

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

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

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

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

Надеется, что это помогает

+0

это должен быть ответ! – nawfal

+0

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

+3

Что-то, о чем я думал, заключается в том, что это может плохо работать для отношений родитель-потомок. В этом случае, я думаю, вам нужно добавить в дочернюю таблицу: parent-pk, parent-guid. В противном случае вы можете потерять ссылки между базами данных. Я не думал об этом слишком много и не делал никакого примера, но это может потребоваться –

5

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

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

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

ID = RECORD СОБРАНА сборником (поле auto_inc)

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

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

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

3

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

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

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

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

+0

Интересное предложение в теории. Это было бы сложно управлять на практике. Более практическое решение, вероятно, будет ответом шворака. –

Смежные вопросы