Как мне хранить GUID в таблицах MySQL? - PullRequest
134 голосов
/ 05 января 2009

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

Ответы [ 10 ]

97 голосов
/ 05 января 2009

Мой администратор базы данных спросил меня, когда я спросил о наилучшем способе хранения GUID для моих объектов, почему мне нужно было хранить 16 байтов, когда я мог сделать то же самое в 4 байта с целым числом. Поскольку он поставил этот вызов передо мной, я подумал, что сейчас самое время упомянуть об этом. Как говорится ...

Вы можете сохранить guid в двоичном виде CHAR (16), если хотите наиболее оптимально использовать пространство хранения.

41 голосов
/ 05 января 2009

Я бы сохранил его как символ (36).

31 голосов
/ 24 августа 2011

В дополнение к ответу 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
24 голосов
/ 05 января 2009

char (36) будет хорошим выбором. Также можно использовать функцию MySQL UUID (), которая возвращает 36-символьный текстовый формат (шестнадцатеричный с дефисами), который можно использовать для получения таких идентификаторов из базы данных.

17 голосов
/ 28 февраля 2014

«Лучше» зависит от того, для чего вы оптимизируете.

Насколько вас заботит размер / производительность хранилища в сравнении с простотой разработки? Что еще более важно - вы генерируете достаточно GUID или выбираете их достаточно часто, чтобы это имело значение?

Если ответ «нет», char(36) более чем достаточно, и это делает хранение / выборку GUID очень простым. В противном случае binary(16) является разумным, но вам придется опираться на MySQL и / или на предпочитаемый вами язык программирования для преобразования туда и обратно из обычного строкового представления.

8 голосов
/ 31 мая 2012

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

7 голосов
/ 08 января 2015

Подпрограмма 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 перед постоянным током, последние байты из первого блока).

Правильный порядок для индексации будет:

1e497556eec5eb6... 
1e497556eec5f10... 
1e497556eec8ddc... 
1e497556eee30d0... 
1e497556efda038... 
1e497556f9641bf... 
1e49755758c3e3e... 

См. Эту статью для поддержки информации: http://mysql.rjweb.org/doc.php/uuid

*** обратите внимание, что я не разделяю ниппель версии с старшими 12 битами метки времени. Это D клев от вашего примера. Я просто бросаю это впереди. Так что моя двоичная последовательность заканчивается тем, что DEFG9ABC и так далее. Это означает, что все мои индексированные UUID начинаются с одного и того же куска. Статья делает то же самое.

5 голосов
/ 26 июня 2015

Для тех, кто только что наткнулся на это, теперь есть гораздо лучшая альтернатива, согласно исследованию Percona.

Он состоит из реорганизации блоков UUID для оптимальной индексации, а затем преобразования в двоичный файл для сокращения объема хранилища.

Читать статью полностью здесь

1 голос
/ 17 декабря 2015

Я бы предложил использовать приведенные ниже функции, так как те, что указаны @ 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 ;
0 голосов
/ 05 ноября 2014

если у вас есть значение char / varchar, отформатированное как стандартный GUID, вы можете просто сохранить его как BINARY (16), используя простой CAST (MyString AS BINARY16), без всех этих ошеломляющих последовательностей CONCAT + SUBSTR.

BINARY (16) поля сравниваются / сортируются / индексируются намного быстрее, чем строки, а также занимают в два раза меньше места в базе данных

...