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

Как хранить GUID в таблицах MySQL?

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

Ответ 3

Добавляя к ответу 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) на самом деле a BINARY(16), выберите свой предпочтительный аромат

Чтобы лучше следовать коду, примите приведенный ниже пример, обозначенный цифрой. (Незаменимые символы используются для иллюстративных целей - каждый из них имеет уникальный характер.) Функции преобразуют порядок байтов, чтобы получить бит-порядок для превосходной кластеризации индексов. Переупорядоченный указатель показан ниже примера.

12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
78563412-BC9A-FGDE-HIJK-LMNOPQRSTUVW

Удалены тире:

123456789ABCDEFGHIJKLMNOPQRSTUVW
78563412BC9AFGDEHIJKLMNOPQRSTUVW

Ответ 4

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

Ответ 5

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

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

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

Ответ 6

Двоичный (16) будет отлично, лучше, чем использование varchar (32).

Ответ 7

Приведенная KCD процедура GuidToBinary должна быть изменена для учета расположения битов метки времени в строке 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, потому что большой 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е до dc, последние байты из первого блока).

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

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

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

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

Ответ 8

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

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

Прочитайте полную статью здесь

Ответ 9

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

Ответ 10

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

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