10 ответов:
мой DBA спросил меня, когда я спросил о лучшем способе хранения GUID для моих объектов, почему мне нужно хранить 16 байт, когда я могу сделать то же самое в 4 байтах с целым числом. Поскольку он бросил мне этот вызов, я подумал, что сейчас самое время упомянуть об этом. Это, как говорится...
вы можете хранить guid в виде двоичного файла CHAR(16), если хотите наиболее оптимально использовать пространство для хранения.
добавляя к ответу 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. (Незаконные символы используются в иллюстративных целях-каждое место уникальный символ.) Функции преобразуют порядок байтов в достижение битового порядка для превосходной кластеризации индексов. Переупорядоченный GUID является показан ниже пример.
12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW 78563412-BC9A-FGDE-HIJK-LMNOPQRSTUVWтире удалено:
123456789ABCDEFGHIJKLMNOPQRSTUVW 78563412BC9AFGDEHIJKLMNOPQRSTUVW
char (36) был бы хорошим выбором. Также можно использовать функцию UUID() MySQL, которая возвращает 36-символьный текстовый формат (hex с дефисами), который может использоваться для извлечения таких идентификаторов из БД.
"лучше" зависит от того, для чего вы оптимизируете.
насколько вы заботитесь о размере/производительности хранилища и простоте разработки? Что еще более важно-вы генерируете достаточно GUID или получаете их достаточно часто, что это имеет значение?
Если ответ "нет",
char(36)более чем достаточно, и это делает хранение / извлечение GUIDs dead-simple. В противном случае,binary(16)разумно, но вам придется опираться на MySQL и / или ваш язык программирования по выбору преобразование назад и вперед из обычного строкового представления.
процедура GuidToBinary, опубликованная KCD, должна быть настроена для учета битового макета метки времени в строке GUID. Если строка представляет UUID версии 1, как те, которые возвращаются подпрограммой UUID () mysql, то компоненты времени встроены в буквы 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, потому что big endian уже дает лучший порядок байтов двоичного индекса. Однако вы хотите, чтобы наиболее значительные байты перемещались перед более низкими байтами. Следовательно, 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 поставил бы его перед 3-м идентификатором (3e перед dc, последние байты из первого блока).
правильный порядок индексации составит:
1e497556eec5eb6... 1e497556eec5f10... 1e497556eec8ddc... 1e497556eee30d0... 1e497556efda038... 1e497556f9641bf... 1e49755758c3e3e...смотрите эту статью для получения дополнительной информации:http://mysql.rjweb.org/doc.php/uuid
*** обратите внимание, что я не разделяйте версию грызть от высоких 12 бит метки времени. Это D грызть из вашего примера. Я просто бросаю его перед собой. Таким образом, моя двоичная последовательность заканчивается DEFG9ABC и так далее. Это означает, что все мои индексированные UUID начинаются с одного и того же укуса. Статьи делает то же самое.
для тех, кто просто наткнулся на это, теперь есть гораздо лучшая альтернатива в соответствии с исследованиями Перконы.
Он состоит из реорганизации кусков UUID для оптимальной индексации, а затем преобразования в двоичный файл для уменьшения объема памяти.
Читать статью полностью здесь
Я бы предложил использовать функции ниже, так как те, которые упомянуты @bigh_29, преобразуют мои GUID в новые (по причинам, которые я не понимаю). Кроме того, они немного быстрее в тестах, которые я сделал на своих столах. 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, вы можете просто сохранить его как двоичный(16) с помощью простого приведения(MyString как BINARY16), без всех этих ошеломляющих последовательностей CONCAT + SUBSTR.
бинарные (16) поля сравниваются / сортируются / индексируются гораздо быстрее строк, а также занимают в два раза меньше места в базе данных
Comments