Benutze ich varchar (36) oder gibt es bessere Möglichkeiten, dies zu tun?
Benutze ich varchar (36) oder gibt es bessere Möglichkeiten, dies zu tun?
Antworten:
Mein DBA fragte mich, als ich nach dem besten Weg zum Speichern von GUIDs für meine Objekte fragte, warum ich 16 Bytes speichern musste, wenn ich dasselbe in 4 Bytes mit einer Ganzzahl tun konnte. Da er mir diese Herausforderung gestellt hat, dachte ich, jetzt sei ein guter Zeitpunkt, sie zu erwähnen. Davon abgesehen ...
Sie können eine Guid als CHAR (16) -Binärdatei speichern, wenn Sie den Speicherplatz optimal nutzen möchten.
Ich würde es als Zeichen speichern (36).
-
s speichern sollten .
Verwenden Sie diese praktischen Funktionen (dank eines klügeren Kollegen von mir), um die Antwort von ThaBadDawg zu ergänzen und von einer Zeichenfolge mit einer Länge von 36 zu einem Byte-Array von 16 zurückzukehren.
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)
ist eigentlich ein BINARY(16)
, wählen Sie Ihren bevorzugten Geschmack
Um dem Code besser zu folgen, nehmen Sie das folgende Beispiel mit der nach Ziffern geordneten GUID. (Unzulässige Zeichen werden zur Veranschaulichung verwendet - jede Stelle enthält ein eindeutiges Zeichen.) Die Funktionen transformieren die Bytereihenfolge, um eine Bitreihenfolge für eine überlegene Indexclusterung zu erreichen. Die neu geordnete Guid wird unter dem Beispiel gezeigt.
12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
78563412-BC9A-FGDE-HIJK-LMNOPQRSTUVW
Striche entfernt:
123456789ABCDEFGHIJKLMNOPQRSTUVW
78563412BC9AFGDEHIJKLMNOPQRSTUVW
GuidToBinary
($ guid char (36)) RETURNS binär (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
und einer BINARY
Äquivalenz verbessert werden ( die Dokumente scheinen zu implizieren, dass es wichtige Unterschiede gibt und eine Erklärung dafür, warum die Leistung von Clustered-Indizes bei neu geordneten Bytes besser ist.
"Besser" hängt davon ab, wofür Sie optimieren.
Wie wichtig ist Ihnen die Speichergröße / -leistung im Vergleich zur einfachen Entwicklung? Noch wichtiger: Generieren Sie genügend GUIDs oder rufen Sie sie häufig genug ab, damit es darauf ankommt?
Wenn die Antwort "Nein" char(36)
lautet , ist dies mehr als gut genug und das Speichern / Abrufen von GUIDs ist kinderleicht. Andernfalls binary(16)
ist dies sinnvoll, aber Sie müssen sich auf MySQL und / oder die Programmiersprache Ihrer Wahl stützen, um von der üblichen Zeichenfolgendarstellung hin und her zu konvertieren.
Binär (16) wäre in Ordnung, besser als die Verwendung von Varchar (32).
Die von KCD veröffentlichte GuidToBinary-Routine sollte angepasst werden, um das Bitlayout des Zeitstempels in der GUID-Zeichenfolge zu berücksichtigen. Wenn die Zeichenfolge eine UUID der Version 1 darstellt, wie sie von der MySQL-Routine uuid () zurückgegeben wird, werden die Zeitkomponenten in die Buchstaben 1-G eingebettet, mit Ausnahme des 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
Wenn Sie in Binär konvertieren, lautet die beste Reihenfolge für die Indizierung: EFG9ABC12345678D + der Rest.
Sie möchten 12345678 nicht gegen 78563412 tauschen, da Big Endian bereits die beste Bytereihenfolge für binäre Indizes liefert. Sie möchten jedoch, dass die höchstwertigen Bytes vor die unteren Bytes verschoben werden. Daher geht EFG zuerst, gefolgt von den mittleren und unteren Bits. Generieren Sie mit uuid () im Laufe einer Minute etwa ein Dutzend UUIDs, und Sie sollten sehen, wie diese Reihenfolge den richtigen Rang ergibt.
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
Die ersten beiden UUIDs wurden zeitnah generiert. Sie variieren nur in den letzten 3 Knabbereien des ersten Blocks. Dies sind die niedrigstwertigen Bits des Zeitstempels, was bedeutet, dass wir sie nach rechts verschieben möchten, wenn wir dies in ein indexierbares Byte-Array konvertieren. Als Gegenbeispiel ist die letzte ID die aktuellste, aber der Austauschalgorithmus der KCD würde sie vor die dritte ID setzen (3e vor DC, letzte Bytes aus dem ersten Block).
Die richtige Reihenfolge für die Indizierung wäre:
1e497556eec5eb6...
1e497556eec5f10...
1e497556eec8ddc...
1e497556eee30d0...
1e497556efda038...
1e497556f9641bf...
1e49755758c3e3e...
Weitere Informationen finden Sie in diesem Artikel: http://mysql.rjweb.org/doc.php/uuid
*** Beachten Sie, dass ich das Versions-Nibble nicht von den hohen 12 Bits des Zeitstempels trenne. Dies ist das D-Knabbern aus Ihrem Beispiel. Ich werfe es einfach vor. Meine binäre Sequenz lautet also DEFG9ABC und so weiter. Dies bedeutet, dass alle meine indizierten UUIDs mit demselben Nibble beginnen. Der Artikel macht das Gleiche.
Für diejenigen, die nur darüber stolpern, gibt es jetzt eine viel bessere Alternative als nach Untersuchungen von Percona.
Es besteht darin, die UUID-Blöcke für eine optimale Indizierung neu zu organisieren und sie dann zur Reduzierung des Speichers in eine Binärdatei umzuwandeln.
Lesen Sie den ganzen Artikel hier
Ich würde vorschlagen, die folgenden Funktionen zu verwenden, da die von @ bigh_29 erwähnten meine Guids in neue umwandeln (aus Gründen, die ich nicht verstehe). Außerdem sind diese in den Tests, die ich an meinen Tischen durchgeführt habe, etwas schneller. 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 ;
Wenn Sie einen char / varchar-Wert als Standard-GUID formatiert haben, können Sie ihn einfach mit dem einfachen CAST (MyString AS BINARY16) als BINARY (16) speichern, ohne all diese umwerfenden Sequenzen von CONCAT + SUBSTR.
BINARY (16) -Felder werden viel schneller als Strings verglichen / sortiert / indiziert und benötigen außerdem zweimal weniger Speicherplatz in der Datenbank
select CAST("hello world, this is as long as uiid" AS BINARY(16));
produzierthello world, thi