Varchar (36) kullanıyor muyum yoksa bunu yapmanın daha iyi bir yolu var mı?
Varchar (36) kullanıyor muyum yoksa bunu yapmanın daha iyi bir yolu var mı?
Yanıtlar:
DBA'm, nesnelerim için GUID'leri saklamanın en iyi yolunu sorduğumda, aynı şeyi bir tamsayı ile 4 baytta yapabildiğimde neden 16 bayt depolamaya ihtiyacım olduğunu sordu. Bana bu meydan okumayı koyduğundan beri, bundan bahsetmek için iyi bir zaman olduğunu düşündüm. Söyleniyor ki...
Depolama alanını en iyi şekilde kullanmak istiyorsanız, bir kılavuzu CHAR (16) ikili dosyası olarak saklayabilirsiniz.
Onu bir karakter olarak saklardım (36).
-
.
ThaBadDawg'ın cevabına ek olarak, 36 uzunluklu dizeden 16 baytlık diziye geri dönmek için bu kullanışlı işlevleri (daha akıllı bir meslektaşım sayesinde) kullanın.
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)
aslında bir BINARY(16)
, tercih ettiğiniz lezzeti seçin
Kodu daha iyi takip etmek için, aşağıdaki basamaklı GUID verilen örneği alın. (Geçersiz karakterler açıklama amacıyla kullanılır - her biri benzersiz bir karakter kullanır.) İşlevler, üstün dizin kümelemesi için bir bit sırası elde etmek üzere bayt sırasını dönüştürür. Yeniden sıralanan kılavuz örneğin altında gösterilmiştir.
12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
78563412-BC9A-FGDE-HIJK-LMNOPQRSTUVW
Kısa çizgiler kaldırıldı:
123456789ABCDEFGHIJKLMNOPQRSTUVW
78563412BC9AFGDEHIJKLMNOPQRSTUVW
GuidToBinary
($ guid char (36)) RETURNS ikili (16) RETURN CONCAT (UNHEX (SUBSTRING ($ guid, 7, 2)), UNHEX (SUBSTRING ($ guid, 5, 2)), UNHEX (SUBSTRING ($ guid, 3, 2)), UNHEX (SUBSTRING ($ guid, 1, 2)), UNHEX (SUBSTRING ($ guid, 12, 2)), UNHEX (SUBSTRING ($ guid, 10, 2)), UNHEX (SUBSTRING ($ guid, 17, 2)), UNHEX (SUBSTRING ($ guid, 15, 2)), UNHEX (SUBSTRING ($ guid, 20, 4)), UNHEX (SUBSTRING ($ guid, 25, 12)));
CHAR
ve BINARY
eşdeğerlik ile geliştirilebileceğini hissediyorum ( dokümanlar önemli farklılıklar olduğunu ve kümelenmiş dizin performansının yeniden sıralanan baytlarla neden daha iyi olduğunu açıklıyor gibi görünüyor.
"Daha iyi" ne için optimizasyon yaptığınıza bağlıdır.
Depolama boyutuna / performansına kıyasla geliştirme kolaylığına ne kadar önem veriyorsunuz? Daha da önemlisi - önemli olan yeterince GUID oluşturuyor veya yeterince sık alıyor musunuz?
Cevap "hayır" ise char(36)
, yeterince iyi ise ve GUID'leri depolamayı / getirmeyi çok basit hale getirir. Aksi takdirde, binary(16)
mantıklıdır, ancak normal dize gösteriminden ileri geri dönüş yapmak için MySQL ve / veya seçtiğiniz programlama diline yaslanmanız gerekir.
İkili (16), varchar (32) kullanımından daha iyi olacaktır.
KCD tarafından yayınlanan GuidToBinary yordamı, GUID dizesindeki zaman damgasının bit düzenini dikkate almak için ayarlanmalıdır. Dize, uuid () mysql yordamı tarafından döndürülenler gibi bir sürüm 1 UUID'yi temsil ediyorsa, zaman bileşenleri D hariç 1-G harflerine gömülür.
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
İkili dosyaya dönüştürdüğünüzde, dizine ekleme için en iyi sipariş şu şekildedir: EFG9ABC12345678D + geri kalanı.
12345678 ile 78563412 arasında geçiş yapmak istemezsiniz, çünkü büyük endian zaten en iyi ikili dizin bayt sırasını verir. Ancak, en önemli baytların alt baytların önüne taşınmasını istiyorsunuz. Bu nedenle EFG önce gelir, ardından orta bitler ve alt bitler gelir. Bir dakika içinde uuid () ile bir düzine kadar UUID oluşturun ve bu siparişin nasıl doğru sıralamayı verdiğini görmelisiniz.
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
İlk iki UUID en yakın zamanda üretildi. Sadece ilk bloğun son 3 kemerinde değişiklik gösterirler. Bunlar zaman damgasının en az önemli bitleridir, yani bunu dizine eklenebilir bir bayt dizisine dönüştürdüğümüzde onları sağa doğru itmek istiyoruz. Bir karşı örnek olarak, son kimlik en güncel olanıdır, ancak KCD'nin takas algoritması bunu 3. ID'den önce yerleştirir (dc'den 3e, ilk bloktan son bayt).
Dizinleme için doğru sıra şu şekildedir:
1e497556eec5eb6...
1e497556eec5f10...
1e497556eec8ddc...
1e497556eee30d0...
1e497556efda038...
1e497556f9641bf...
1e49755758c3e3e...
Destekleyici bilgiler için bu makaleye bakın: http://mysql.rjweb.org/doc.php/uuid
*** Ben zaman damgası yüksek 12 bit sürümü nibble bölünmedi unutmayın. Bu, örneğinizdeki D kemeri. Sadece öne atıyorum. Böylece ikili dizilim DEFG9ABC ve benzeri oluyor. Bu, tüm dizinlenmiş UUID'lerimin aynı uç ile başladığını gösterir. Makale de aynı şeyi yapıyor.
Bunun karşısında tökezleyenler için, şimdi Percona'nın araştırmasına göre çok daha iyi bir alternatif var.
Optimal indeksleme için UUID parçalarının yeniden düzenlenmesinden ve daha az depolama için ikili dosyaya dönüştürülmesinden oluşur.
Makalenin tamamını buradan okuyun
@ Bigh_29 tarafından belirtilenler kılavuzlarımı yenilerine dönüştürdüğünden (anlamadığım nedenlerden dolayı) aşağıdaki işlevleri kullanmanızı öneririm. Ayrıca, bunlar tablolarımda yaptığım testlerde biraz daha hızlı. 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 ;
standart GUID olarak biçimlendirilmiş bir char / varchar değeriniz varsa, basit CAST (BINARY16 olarak MyString AS) kullanarak tüm CONCAT + SUBSTR'ın akıl almaz dizileri olmadan bunu BINARY (16) olarak depolayabilirsiniz.
BINARY (16) alanları, dizelerden çok daha hızlı karşılaştırılır / sıralanır / dizine eklenir ve ayrıca veritabanında iki kat daha az yer kaplar
select CAST("hello world, this is as long as uiid" AS BINARY(16));
üretirhello world, thi