Чи використовую я varchar (36) чи є кращі способи це зробити?
Чи використовую я varchar (36) чи є кращі способи це зробити?
Відповіді:
Мій DBA запитав мене, коли я запитував про найкращий спосіб зберігання GUID для моїх об’єктів, чому мені потрібно зберігати 16 байт, коли я можу зробити те саме в 4 байтах із цілим числом. Оскільки він поставив мені це завдання, я подумав, що зараз вдалий час згадати про це. Це сумно...
Ви можете зберігати вказівник як двійковий файл CHAR (16), якщо хочете максимально оптимально використовувати простір для зберігання.
Я б зберігав це як чар (36).
-
s.
Додавши відповідь 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
($ GUI char (36)) ВІДПОВІДАЄТЬСЯ бінарний (16) ПОВЕРНЕННЯ CONCAT (UNHEX (SUBSTRING ($ Guid, 7, 2)), UNHEX (SUBSTRING ($ guide, 5, 2)), UNHEX (SUBSTRING ($ guide, 3, 2)), UNHEX (SUBSTRING ($ guide, 1, 2)), UNHEX (SUBSTRING ($ guide, 12, 2)), UNHEX (SUBSTRING ($ guide, 10, 2)), UNHEX (SUBSTRING ($ Guid, 17, 2)), UNHEX (SUBSTRING ($ guide, 15, 2)), UNHEX (SUBSTRING ($ Guid, 20, 4)), UNHEX (ПІДПРИЄМСТВО) ($ Guid, 25, 12)));
CHAR
та BINARY
еквівалентності ( документи, мабуть, означають, що існують важливі відмінності та пояснення того, чому ефективність кластеризованого індексу краща з перепорядкованими байтами.
"Краще" залежить від того, для чого ви оптимізуєте.
Наскільки ви дбаєте про розмір / продуктивність сховища та простість розробки? Що ще важливіше - ви генеруєте достатню кількість GUID або вибираєте їх досить часто, щоб це було важливо?
Якщо відповідь "ні", char(36)
це більш ніж добре, і зберігання / отримання GUID-файлів робить простою. В іншому випадку binary(16)
це розумно, але вам доведеться спиратися на MySQL та / або вашу мову програмування на вибір, щоб конвертувати назад і назад зі звичайного представлення рядків.
Бінарне (16) було б добре, краще, ніж використання вархара (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, оскільки великий ендіан вже дає найкращий порядок байт бінарних індексів. Однак ви хочете, щоб найбільш значні байти переміщувались перед нижчими байтами. Отже, спочатку йде 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, перетворюють мої посібники в нові (з причин, яких я не розумію). Крім того, це трохи швидше в тестах, які я робив на своїх столах. 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