Come devo memorizzare il GUID nelle tabelle MySQL?


146

Uso varchar (36) o ci sono modi migliori per farlo?


1
"thaBadDawg" offre una buona risposta. Esiste un thread parallelo su Stack Overflow che discute l'argomento. Ho aggiunto alcuni commenti a quei thread che rispondono a quel link alle risorse con maggiori dettagli. Ecco il link alla domanda: stackoverflow.com/questions/547118/storing-mysql-guid-uuids - Mi aspetto che questo argomento diventi più comune quando le persone iniziano a considerare AWS e Aurora.
Zack Jannsen,

Risposte:


104

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.


176
Perché con 16 byte, puoi generare cose in database diversi, su macchine diverse, in momenti diversi e unire i dati senza soluzione di continuità :)
Billy ONeal

4
bisogno di risposta, cos'è veramente un binario char 16? non char? non binario? Non vedo quel tipo in nessuno degli strumenti di mysql gui, né in alcuna documentazione nel sito mysql. @BillyONeal
nawfal,

3
@nawfal: Char è il tipo di dati. BINARY è l'identificatore di tipo rispetto al tipo. L'unico effetto che ha è di modificare il modo in cui MySQL esegue le regole di confronto. Vedere dev.mysql.com/doc/refman/5.0/en/charset-binary-op.html per maggiori dettagli. Ovviamente puoi semplicemente usare un tipo BINARY direttamente se lo strumento di modifica del database ti consente di farlo. (Gli strumenti meno recenti non conoscono il tipo di dati binari ma conoscono il flag della colonna binaria)
Billy ONeal

2
un campo CHAR e un campo BINARY sono essenzialmente gli stessi. Se vuoi portarlo ai livelli più elementari, un CHAR è un campo binario che si aspetta un valore da 0 a 255 con l'intenzione di rappresentare detto valore con un valore mappato da una tabella di ricerca (nella maggior parte dei casi ora, UTF8). Un campo BINARY prevede lo stesso tipo di valore senza alcuna intenzione di rappresentare tali dati da una tabella di ricerca. Ho usato CHAR (16) nei giorni 4.x perché allora MySQL non era buono come lo è ora.
thaBadDawg,

15
Esistono diversi buoni motivi per cui un GUID è molto meglio di un autoincremento. Jeff Atwood elenca questi . Per me, il miglior vantaggio nell'uso di un GUID è che la mia app non avrà bisogno di un roundtrip di database per conoscere la chiave di un'entità: potrei popolarla a livello di programmazione, cosa che non potrei fare se stessi usando un campo di auto-incremento. Questo mi ha salvato da diversi mal di testa: con GUID posso gestire l'entità allo stesso modo, indipendentemente dal fatto che l'entità sia già stata persistita o sia nuova.
Arialdo Martini,

48

Lo memorizzerei come carattere (36).


5
Non riesco a capire perché dovresti conservare -i messaggi di posta elettronica .
Afshin Mehrabani,

2
@AfshinMehrabani È semplice, diretto, leggibile dall'uomo. Non è necessario, ovviamente, ma se la memorizzazione di quei byte extra non fa male, questa è la soluzione migliore.
user1717828

2
La memorizzazione dei trattini potrebbe non essere una buona idea perché causerà un sovraccarico. Se vuoi renderlo leggibile, fai leggere l'applicazione con i trattini.
Lucca Ferri,

@AfshinMehrabani un'altra considerazione sta analizzandola dal database. La maggior parte delle implementazioni prevede trattini in una guida valida.
Ryan Gates,

È possibile inserire i trattini durante il recupero per convertire facilmente un carattere (32) in carattere (36). usa l'Inserisci FN di mySql.
Joedotnot

33

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

Ecco il GuidToBinary sopra senza rimuovere i trattini dalla stringa: CREATE FUNCTION 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)));
Jonathan Oliver,

4
Per i curiosi, queste funzioni sono superiori a UNHEX (REPLACE (UUID (), '-', '')) perché organizza i bit in un ordine che funzionerà meglio in un indice cluster.
Slashterix,

Questo è molto utile, ma penso che potrebbe essere migliorato con una fonte CHARe BINARYun'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.
Patrick M

Quando uso questo mio guid è cambiato. Ho provato a inserirlo usando sia unhex (sostituisci (stringa, '-', '')) che la funzione sopra e quando li riconvertisco usando gli stessi metodi, il guid che è selezionato non è quello che è stato inserito. Cosa sta trasformando il guid? Tutto quello che ho fatto è stato copiato il codice dall'alto.
vsdev,

@JonathanOliver Potresti condividere il codice per la funzione BinaryToGuid ()?
Arun Avanathan,

27

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.


19

"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.


2
Se si ospita il software (ad esempio una pagina Web) e non si vende / installa nel client, è sempre possibile iniziare con char (36) per un facile sviluppo nella fase iniziale del software e passare a un formato più compatto formato man mano che il sistema cresce nell'uso e inizia a richiedere l'ottimizzazione.
Xavi Montero,

1
Il lato negativo più grande del carattere molto più grande (36) è quanto spazio occuperà l'indice. Se si dispone di un numero elevato di record nel database, si raddoppia la dimensione dell'indice.
bpeikes,


7

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.


è lo scopo di questo risparmiare spazio di archiviazione? o per renderli utili?
MD004,

1
@ MD004. Crea un indice di ordinamento migliore. Lo spazio rimane lo stesso.
bigh_29

5

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


Ho letto quell'articolo prima. Lo trovo molto interessante, ma allora come dovremmo eseguire una query se vogliamo filtrare per un ID che è binario? Immagino che dovremo esadecimare di nuovo e quindi applicare i criteri. È così impegnativo? Perché memorizzare binario (16) (sicuro che è meglio di varchar (36)) invece di bigint di 8 byte?
Massimo Decimo

2
C'è un articolo aggiornato di MariaDB che dovrebbe rispondere alla tua domanda mariadb.com/kb/en/mariadb/guiduuid-performance
sleepycal

in seguito, UUIDv4 è completamente casuale e non necessita di blocchi.
Mahmoud Al-Qudsi,

2

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 ;

-4

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


2
L'esecuzione di questa query mostra che CAST converte la stringa uuid in byte ASCII: set @a = uuid (); seleziona @a, hex (cast (@a AS BINARY (16))); Ottengo 16f20d98-9760-11e4-b981-feb7b39d48d6: 3136663230643938 2D 39373630 2D 3131 (spazi aggiunti per la formattazione). 0x31 = ascii 1, 0x36 = ascii 6. Otteniamo anche 0x2D, ​​che è il trattino. Questo non è molto diverso dalla semplice memorizzazione di guid come stringa, tranne per il fatto che la stringa viene troncata al sedicesimo carattere, che separa la parte dell'ID specifica della macchina.
bigh_29,

Sì, questo è semplicemente troncamento. select CAST("hello world, this is as long as uiid" AS BINARY(16));producehello world, thi
MD004,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.