Как я должен хранить GUID в таблицах MySQL?



использую ли я varchar (36) или есть ли лучшие способы сделать это?

708   10  

10 ответов:

мой DBA спросил меня, когда я спросил о лучшем способе хранения 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. (Незаконные символы используются в иллюстративных целях-каждое место уникальный символ.) Функции преобразуют порядок байтов в достижение битового порядка для превосходной кластеризации индексов. Переупорядоченный 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 и / или ваш язык программирования по выбору преобразование назад и вперед из обычного строкового представления.

Binary(16) было бы хорошо, лучше, чем использование varchar (32).

процедура 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

    Ничего не найдено.