Подтвердить что ты не робот

MySQL PRIMARY KEYs: UUID/GUID против BIGINT (временная метка + случайная)

tl; dr: Является ли назначение идентификаторов строк {unixtimestamp} {randomdigits} (например, 1308022796123456) как BIGINT хорошей идеей, если я не хочу иметь дело с UUID?

Просто интересно, есть ли у кого-нибудь представление о производительности или других технических соображениях/ограничениях в отношении идентификаторов /PRIMARY KEY, назначенных для записей базы данных на нескольких серверах.

Мое приложение PHP + MySQL работает на нескольких серверах, и данные должны быть объединены. Таким образом, я перерос стандартный метод последовательного/автоматического_инкремента для определения строк.

Мои исследования в решении привели меня к концепции использования UUID/GUID. Однако необходимость изменить мой код для работы с преобразованием строк UUID в двоичные значения в MySQL кажется немного болью/работой. Я не хочу хранить UUID как VARCHAR для хранения и повышения производительности.

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

В качестве промежуточного уровня я придумал идею создания идентификационных колонок BIGINT и назначение идентификаторов с использованием текущей временной отметки unix, а затем 6 случайных цифр. Так что скажем, что мое случайное число составило 123456, мой сгенерированный идентификатор сегодня выйдет как: 1308022796123456

Один из 10 миллионов шансов на конфликт для строк, созданных в течение одной секунды, отлично со мной. Я не делаю создания массового ряда быстро.

Одна проблема, которую я прочитал со случайно генерируемыми UUID, заключается в том, что они плохо относятся к индексам, поскольку значения не являются последовательными (они разбросаны повсюду). Функция UUID() в MySQL обращается к этому путем генерации первой части UUID с текущей временной метки. Поэтому я скопировал эту идею с отметкой unix в начале моего BIGINT. Будут ли мои индексы медленными?

Плюсы моей идеи BIGINT:

  • Дает мне преимущества для нескольких серверов/слияния UUID.
  • Требуется очень малое изменение кода приложения (все уже запрограммировано для обработки целых чисел для идентификаторов)
  • Половина памяти UUID (8 байт против 16 байт)

Минусы:

  • ??? - Пожалуйста, дайте мне знать, если вы можете думать о них.

Некоторые последующие вопросы, чтобы согласиться с этим:

  • Должен ли я использовать более или менее 6 случайных цифр в конце? Будет ли это иметь значение для производительности индекса?

  • Является ли один из этих методов "случайным"?: Получение PHP для генерации 6 цифр и объединения их вместе. - Получение PHP для генерации числа в диапазоне 1 - 999999, а затем zerofilling для обеспечения 6 цифр.

Спасибо за любые советы. Извините за стену текста.

4b9b3361

Ответ 1

Я столкнулся с этой проблемой в своей профессиональной жизни. Мы использовали timestamp + random number и столкнулись с серьезными проблемами, когда наши приложения расширялись (больше клиентов, больше серверов, больше запросов). Конечно, мы (тупо) использовали только 4 цифры, а затем изменили на 6, но вы были бы удивлены, как часто ошибки все же происходят.

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

Используя UUID, ваш индексный размер будет увеличиваться, а больший индекс приведет к снижению производительности (возможно, незаметной, но более бедной). Однако MySQL поддерживает собственный тип UUID (никогда не используйте varchar в качестве первичного ключа!) И может эффективно обрабатывать индексирование, поиск и т.д., Даже по сравнению с bigint. Наибольшая производительность, связанная с вашим индексом, - это почти всегда число индексированных строк, а не размер элемента, являющегося индексом (если вы не хотите индексировать длинный текст или что-то нелепо подобное).

Чтобы ответить на ваш вопрос: Bigint (со случайными номерами прилагается) будет одобрен, если вы не планируете значительно масштабировать свое приложение/услугу. Если ваш код может обрабатывать изменения без значительных изменений, и ваше приложение не будет взорваться при возникновении дублирующей ключевой ошибки, пойдите с ним. В противном случае, bite-the-bullet и перейдите к более существенному варианту.

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

Ответ 2

Вы можете вручную изменить начальный номер автонабора.

ALTER TABLE foo AUTO_INCREMENT = ####

Беззнаковый int может хранить до 4 294 967 295, позволяет округлить его до 4 290 000 000.

Используйте первые 3 цифры для серийного номера сервера и последние 7 цифр для идентификатора строки.

Это дает вам до 430 серверов (включая 000) и до 10 миллионов идентификаторов для каждого сервера.

Итак, для сервера # 172 вы вручную меняете номер автонабора, чтобы начать с 1,720,000,000, затем пусть он последовательно назначает идентификаторы.

Если вы считаете, что у вас может быть больше серверов, но меньше идентификаторов на сервер, затем настройте их на 4 цифры на сервер и 6 для ID (то есть до 1 миллиона идентификаторов).

Вы также можете разбить число, используя двоичные цифры вместо десятичных цифр (возможно, 10 двоичных цифр на сервер и 22 для идентификатора. Так, например, сервер 76 начинается с 2 ^ 22 * ​​76 = 318,767,104 и заканчивается на 322,961,407).

В этом случае вам даже не нужен четкий раскол. Возьмите 4,294,967,295, разделите его на максимальное количество серверов, которые, по вашему мнению, когда-либо будут, и что ваш интервал.

Вы можете использовать bigint, если считаете, что вам нужно больше идентификаторов, но это очень большое количество.

Ответ 3

Если вы хотите использовать метод отметки времени, сделайте следующее:

Дайте каждому серверу номер, к которому добавляется идентификатор профайла приложения, которое выполняет вставку (или идентификатор потока) (в PHP это getmypid()), а затем добавляет, сколько времени этот процесс был жив/активен для (в PHP это getrusage()) и, наконец, добавляет счетчик, который начинается с 0 в начале каждого вызова script (т.е. каждая вставка в пределах того же script добавляет один к нему).

Кроме того, вам не нужно хранить полную временную метку unix - большинство из этих цифр относятся к году 2011 года, а не к 1970 году. Поэтому, если вы не можете получить число, говорящее, как долго этот процесс был жив для, то, по крайней мере, вычесть фиксированную временную метку, представляемую сегодня - таким образом вам понадобится гораздо меньше цифр.

Ответ 4

Используйте GUID в качестве уникального индекса, но также рассчитайте 64-битный (BIGINT) хэш GUID, сохраните его в отдельном столбце и проиндексируйте. Чтобы получить, извлеките несколько записей, которые соответствуют столбцу хэша, а затем используйте GUID, чтобы найти правильный.