Uso varchar (36) o ci sono modi migliori per farlo?
Uso varchar (36) o ci sono modi migliori per farlo?
Risposte:
Il mio DBA mi ha chiesto quando ho chiesto il modo migliore per archiviare i GUID per i miei oggetti perché avevo bisogno di memorizzare 16 byte quando potevo fare la stessa cosa in 4 byte con un intero. Da quando mi ha dato quella sfida, ho pensato che fosse un buon momento per menzionarla. Detto ciò...
È possibile memorizzare un guid come binario CHAR (16) se si desidera utilizzare in modo ottimale lo spazio di archiviazione.
Lo memorizzerei come carattere (36).
-
i messaggi di posta elettronica .
Aggiungendo alla risposta di ThaBadDawg, utilizzare queste utili funzioni (grazie a un mio collega più saggio) per tornare da una stringa di 36 lunghezze a un array di byte di 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)
è in realtà un BINARY(16)
, scegli il tuo gusto preferito
Per seguire meglio il codice, prendi l'esempio dato il GUID ordinato per le cifre di seguito. (I caratteri illegali vengono utilizzati a scopo illustrativo - ogni posizione ha un carattere univoco.) Le funzioni trasformeranno l'ordinamento dei byte per ottenere un ordine di bit per un clustering di indice superiore. La guida riordinata è mostrata sotto l'esempio.
12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
78563412-BC9A-FGDE-HIJK-LMNOPQRSTUVW
Tratte rimosse:
123456789ABCDEFGHIJKLMNOPQRSTUVW
78563412BC9AFGDEHIJKLMNOPQRSTUVW
GuidToBinary
($ guid char (36)) RETURNS binario (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
e BINARY
un'equivalenza ( i documenti sembrano implicare che ci sono differenze importanti e una spiegazione del perché le prestazioni dell'indice cluster sono migliori con byte riordinati.
char (36) sarebbe una buona scelta. È inoltre possibile utilizzare la funzione UUID () di MySQL che restituisce un formato di testo di 36 caratteri (hex con trattini) che può essere utilizzato per il recupero di tali ID dal db.
"Meglio" dipende da cosa stai ottimizzando.
Quanto ti importa delle dimensioni / prestazioni dello spazio di archiviazione rispetto alla facilità di sviluppo? Ancora più importante: stai generando abbastanza GUID, o li recuperi abbastanza frequentemente, che conta?
Se la risposta è "no", char(36)
è più che sufficiente e rende estremamente semplice la memorizzazione / il recupero di GUID. Altrimenti, binary(16)
è ragionevole, ma dovrai affidarti a MySQL e / o al tuo linguaggio di programmazione preferito per convertire avanti e indietro dalla solita rappresentazione di stringhe.
Binario (16) andrebbe bene, meglio dell'uso di varchar (32).
La routine GuidToBinary pubblicata da KCD dovrebbe essere ottimizzata per tenere conto del layout dei bit del timestamp nella stringa GUID. Se la stringa rappresenta un UUID versione 1, come quelli restituiti dalla routine uuid () mysql, i componenti temporali sono incorporati nelle lettere 1-G, escluso 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
Quando si converte in binario, l'ordine migliore per l'indicizzazione sarebbe: EFG9ABC12345678D + il resto.
Non si desidera scambiare 12345678 a 78563412 perché big endian produce già il miglior ordine di byte dell'indice binario. Tuttavia, si desidera spostare i byte più significativi di fronte ai byte inferiori. Quindi, EFG inizia per primo, seguito dai bit centrali e dai bit inferiori. Genera una dozzina di UUID con uuid () nel corso di un minuto e dovresti vedere come questo ordine produce il rango corretto.
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
I primi due UUID sono stati generati più vicini nel tempo. Variano solo negli ultimi 3 stuzzichini del primo blocco. Questi sono i bit meno significativi del timestamp, il che significa che vogliamo spingerli a destra quando lo convertiamo in un array di byte indicizzabile. Come esempio di contatore, l'ultimo ID è il più attuale, ma l'algoritmo di scambio del KCD lo metterebbe prima del terzo ID (3e prima di cc, ultimi byte dal primo blocco).
L'ordine corretto per l'indicizzazione sarebbe:
1e497556eec5eb6...
1e497556eec5f10...
1e497556eec8ddc...
1e497556eee30d0...
1e497556efda038...
1e497556f9641bf...
1e49755758c3e3e...
Consulta questo articolo per informazioni di supporto: http://mysql.rjweb.org/doc.php/uuid
*** nota che non divido il nibble di versione dagli alti 12 bit del timestamp. Questo è il bocconcino D del tuo esempio. Lo lancio solo davanti. Quindi la mia sequenza binaria finisce per essere DEFG9ABC e così via. Ciò implica che tutti i miei UUID indicizzati iniziano con lo stesso stuzzichino. L'articolo fa la stessa cosa.
Per quelli che si imbattono in questo, ora c'è un'alternativa molto migliore secondo la ricerca di Percona.
Consiste nella riorganizzazione dei blocchi UUID per l'indicizzazione ottimale, quindi nella conversione in binario per una memoria ridotta.
Leggi l'articolo completo qui
Suggerirei di utilizzare le funzioni seguenti poiché quelle menzionate da @ bigh_29 trasformano le mie guide in nuove (per motivi che non capisco). Inoltre, questi sono un po 'più veloci nei test che ho fatto sui miei tavoli. 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 ;
se hai un valore char / varchar formattato come GUID standard, puoi semplicemente memorizzarlo come BINARY (16) usando il semplice CAST (MyString AS BINARY16), senza tutte quelle sequenze strabilianti di CONCAT + SUBSTR.
I campi BINARY (16) vengono confrontati / ordinati / indicizzati molto più velocemente delle stringhe e occupano anche due volte meno spazio nel database
select CAST("hello world, this is as long as uiid" AS BINARY(16));
producehello world, thi