Я использую varchar (36) или есть ли лучшие способы сделать это?
Я использую varchar (36) или есть ли лучшие способы сделать это?
Ответы:
Мой администратор базы данных спросил меня, когда я спросил о наилучшем способе хранения GUID для моих объектов, почему мне нужно было хранить 16 байтов, когда я мог сделать то же самое в 4 байта с целым числом. Поскольку он поставил мне этот вызов, я подумал, что сейчас самое время упомянуть об этом. Что, как говорится...
Вы можете хранить guid как двоичный файл CHAR (16), если хотите наиболее оптимально использовать пространство для хранения.
Я бы сохранил его как символ (36).
-
с.
В дополнение к ответу ThaBadDawg, используйте эти удобные функции (благодаря моему более мудрому коллеге), чтобы получить строку длиной 36 обратно в массив байтов из 16.
DELIMITER $$
CREATE FUNCTION `GuidToBinary`(
$Data VARCHAR(36)
) RETURNS binary(16)
DETERMINISTIC
NO SQL
BEGIN
DECLARE $Result BINARY(16) DEFAULT NULL;
IF $Data IS NOT NULL THEN
SET $Data = REPLACE($Data,'-','');
SET $Result =
CONCAT( UNHEX(SUBSTRING($Data,7,2)), UNHEX(SUBSTRING($Data,5,2)),
UNHEX(SUBSTRING($Data,3,2)), UNHEX(SUBSTRING($Data,1,2)),
UNHEX(SUBSTRING($Data,11,2)),UNHEX(SUBSTRING($Data,9,2)),
UNHEX(SUBSTRING($Data,15,2)),UNHEX(SUBSTRING($Data,13,2)),
UNHEX(SUBSTRING($Data,17,16)));
END IF;
RETURN $Result;
END
$$
CREATE FUNCTION `ToGuid`(
$Data BINARY(16)
) RETURNS char(36) CHARSET utf8
DETERMINISTIC
NO SQL
BEGIN
DECLARE $Result CHAR(36) DEFAULT NULL;
IF $Data IS NOT NULL THEN
SET $Result =
CONCAT(
HEX(SUBSTRING($Data,4,1)), HEX(SUBSTRING($Data,3,1)),
HEX(SUBSTRING($Data,2,1)), HEX(SUBSTRING($Data,1,1)), '-',
HEX(SUBSTRING($Data,6,1)), HEX(SUBSTRING($Data,5,1)), '-',
HEX(SUBSTRING($Data,8,1)), HEX(SUBSTRING($Data,7,1)), '-',
HEX(SUBSTRING($Data,9,2)), '-', HEX(SUBSTRING($Data,11,6)));
END IF;
RETURN $Result;
END
$$
CHAR(16)
на самом деле BINARY(16)
, выберите предпочтительный вкус
Чтобы лучше следовать коду, возьмите пример с приведенным ниже порядковым номером GUID. (Недопустимые символы используются в иллюстративных целях - каждый из них содержит уникальный символ.) Функции преобразуют порядок байтов для достижения порядка битов для превосходной кластеризации индекса. Переупорядоченная направляющая показана под примером.
12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
78563412-BC9A-FGDE-HIJK-LMNOPQRSTUVW
Черточки удалены:
123456789ABCDEFGHIJKLMNOPQRSTUVW
78563412BC9AFGDEHIJKLMNOPQRSTUVW
GuidToBinary
($ guid char (36)) RETURNS binary (16) RETURN CONCAT (UNHEX (SUBSTRING ($ guid, 7, 2))), UNHEX (SUBSTRING ($ guid, 5, 2)), UNHEX (SUBSTRING ($ guid, 3, 2)), UNHEX (SUBSTRING ($ guid, 1, 2)), UNHEX (SUBSTRING ($ guid, 12, 2)), UNHEX (SUBSTRING ($ guid, 10, 2)), UNHEX (SUBSTRING ($ guid, 17, 2)), UNHEX (SUBSTRING ($ guid, 15, 2)), UNHEX (SUBSTRING ($ guid, 20, 4))), UNHEX (SUBSTRING ($ guid, 25, 12)));
CHAR
и BINARY
эквивалентности ( документы, по- видимому, подразумевают, что существуют важные различия и объяснение того, почему производительность кластерного индекса лучше с переупорядоченными байтами.
«Лучше» зависит от того, для чего вы оптимизируете.
Насколько вы заботитесь о размере / производительности хранилища в сравнении с простотой разработки? Что еще более важно - вы генерируете достаточно GUID или загружаете их достаточно часто, чтобы это имело значение?
Если ответ «нет», char(36)
это более чем достаточно, и это делает хранение / выборку GUID очень простым. В противном случае binary(16)
это разумно, но вам придется опираться на MySQL и / или на предпочитаемый вами язык программирования, чтобы конвертировать туда и обратно из обычного строкового представления.
Двоичный (16) будет хорошо, лучше, чем использование varchar (32).
Подпрограмма GuidToBinary, опубликованная KCD, должна быть настроена так, чтобы учитывать расположение битов временной метки в строке GUID. Если строка представляет UUID версии 1, как те, которые возвращаются подпрограммой mysql uuid (), то временные компоненты включаются в буквы 1-G, исключая D.
12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
12345678 = least significant 4 bytes of the timestamp in big endian order
9ABC = middle 2 timestamp bytes in big endian
D = 1 to signify a version 1 UUID
EFG = most significant 12 bits of the timestamp in big endian
Когда вы преобразуете в двоичный файл, лучшим порядком для индексации будет: EFG9ABC12345678D + остальное.
Вы не хотите поменять местами 12345678 на 78563412, потому что больший порядок байтов уже дает лучший порядок байтов двоичного индекса. Однако вы хотите, чтобы наиболее значимые байты были перемещены перед младшими байтами. Следовательно, EFG идет первым, затем идут средние и младшие биты. Создайте дюжину UUID с помощью uuid () в течение минуты, и вы должны увидеть, как этот порядок дает правильный ранг.
select uuid(), 0
union
select uuid(), sleep(.001)
union
select uuid(), sleep(.010)
union
select uuid(), sleep(.100)
union
select uuid(), sleep(1)
union
select uuid(), sleep(10)
union
select uuid(), 0;
/* output */
6eec5eb6-9755-11e4-b981-feb7b39d48d6
6eec5f10-9755-11e4-b981-feb7b39d48d6
6eec8ddc-9755-11e4-b981-feb7b39d48d6
6eee30d0-9755-11e4-b981-feb7b39d48d6
6efda038-9755-11e4-b981-feb7b39d48d6
6f9641bf-9755-11e4-b981-feb7b39d48d6
758c3e3e-9755-11e4-b981-feb7b39d48d6
Первые два UUID были сгенерированы ближе всего по времени. Они различаются только в последних 3 полубайтах первого блока. Это наименее значимые биты метки времени, что означает, что мы хотим сдвинуть их вправо, когда преобразуем это в индексируемый байтовый массив. В качестве встречного примера последний идентификатор является самым текущим, но алгоритм обмена KCD поместил бы его перед третьим идентификатором (3e перед dc, последние байты из первого блока).
Правильный порядок для индексации будет:
1e497556eec5eb6...
1e497556eec5f10...
1e497556eec8ddc...
1e497556eee30d0...
1e497556efda038...
1e497556f9641bf...
1e49755758c3e3e...
См. Эту статью для поддержки информации: http://mysql.rjweb.org/doc.php/uuid
*** обратите внимание, что я не разделяю ниппель версии с старшими 12 битами метки времени. Это D клев от вашего примера. Я просто бросаю это впереди. Так что моя двоичная последовательность заканчивается тем, что DEFG9ABC и так далее. Это означает, что все мои проиндексированные UUID начинаются с одного и того же куска. Статья делает то же самое.
Для тех, кто только что наткнулся на это, теперь есть гораздо лучшая альтернатива, согласно исследованию Percona.
Он состоит из реорганизации блоков UUID для оптимальной индексации, а затем преобразования в двоичный файл для сокращения объема хранения.
Прочитайте полную статью здесь
Я бы предложил использовать функции, указанные ниже, так как те, что упомянуты @ bigh_29, преобразуют мои направляющие в новые (по причинам, которые я не понимаю). Кроме того, они немного быстрее в тех тестах, которые я проводил на своих столах. https://gist.github.com/damienb/159151
DELIMITER |
CREATE FUNCTION uuid_from_bin(b BINARY(16))
RETURNS CHAR(36) DETERMINISTIC
BEGIN
DECLARE hex CHAR(32);
SET hex = HEX(b);
RETURN LOWER(CONCAT(LEFT(hex, 8), '-', MID(hex, 9,4), '-', MID(hex, 13,4), '-', MID(hex, 17,4), '-', RIGHT(hex, 12)));
END
|
CREATE FUNCTION uuid_to_bin(s CHAR(36))
RETURNS BINARY(16) DETERMINISTIC
RETURN UNHEX(CONCAT(LEFT(s, 8), MID(s, 10, 4), MID(s, 15, 4), MID(s, 20, 4), RIGHT(s, 12)))
|
DELIMITER ;
если у вас есть значение char / varchar, отформатированное как стандартный GUID, вы можете просто сохранить его как BINARY (16), используя простой CAST (MyString AS BINARY16), без всех этих ошеломляющих последовательностей CONCAT + SUBSTR.
BINARY (16) поля сравниваются / сортируются / индексируются намного быстрее, чем строки, а также занимают в два раза меньше места в базе данных
select CAST("hello world, this is as long as uiid" AS BINARY(16));
производитhello world, thi