Qual è l'impatto sulle prestazioni dell'utilizzo di CHAR vs VARCHAR su un campo di dimensioni fisse?


58

Ho una colonna indicizzata che memorizza un hash MD5. Pertanto, la colonna memorizzerà sempre un valore di 32 caratteri. Per qualsiasi motivo, questo è stato creato come varchar piuttosto che come carattere. Vale la pena di migrare il database per convertirlo in un carattere? Questo è in MySQL 5.0 con InnoDB.


6
ATTENZIONE Questa domanda e le sue risposte sono state scritte prima che InnoDB e utf8 fossero le impostazioni predefinite.
Rick James,

Risposte:


56

Una domanda simile è stata posta prima

Implicazioni sulle prestazioni delle dimensioni MySQL VARCHAR

Ecco l'estratto della mia risposta

È necessario realizzare i compromessi dell'utilizzo di CHAR vs VARCHAR

Con i campi CHAR, ciò che assegni è esattamente quello che ottieni. Ad esempio, CHAR (15) alloca e memorizza 15 byte, indipendentemente dal carattere inserito nel campo. La manipolazione delle stringhe è semplice e diretta poiché la dimensione del campo dati è totalmente prevedibile.

Con i campi VARCHAR, ottieni una storia completamente diversa. Ad esempio VARCHAR (15) alloca effettivamente in modo dinamico fino a 16 byte, fino a 15 per i dati e, almeno, 1 byte aggiuntivo per memorizzare la lunghezza dei dati. Se hai la stringa 'ciao' da memorizzare che richiederà 6 byte, non 5. La manipolazione della stringa deve sempre eseguire una qualche forma di controllo della lunghezza in tutti i casi.

Il compromesso è più evidente quando si fanno due cose: 1. Memorizzare milioni o miliardi di righe 2. Indicizzare colonne che sono CHAR o VARCHAR

TRADEOFF # 1 Ovviamente, VARCHAR ha il vantaggio dato che i dati a lunghezza variabile produrrebbero file più piccole e, quindi, file fisici più piccoli.

TRADEOFF # 2 Poiché i campi CHAR richiedono una minore manipolazione delle stringhe a causa di larghezze di campo fisse, le ricerche dell'indice rispetto al campo CHAR sono in media il 20% più veloci di quelle dei campi VARCHAR. Questa non è alcuna congettura da parte mia. Il libro MySQL Database Design and Tuning ha eseguito qualcosa di meraviglioso su una tabella MyISAM per dimostrarlo. L'esempio nel libro ha fatto qualcosa di simile al seguente:

ALTER TABLE tblname ROW_FORMAT=FIXED;

Questa direttiva forza tutti i VARCHAR a comportarsi come CHAR. L'ho fatto nel mio precedente lavoro nel 2007 e ho preso un tavolo da 300 GB e accelerato la ricerca dell'indice del 20%, senza cambiare nient'altro. Ha funzionato come pubblicato. Tuttavia, ha prodotto un tavolo di dimensioni quasi doppie, ma questo risale semplicemente al compromesso n. 1.

È possibile analizzare i dati archiviati per vedere cosa consiglia MySQL per la definizione di colonna. Basta eseguire quanto segue su qualsiasi tabella:

SELECT * FROM tblname PROCEDURE ANALYSE();

Ciò attraverserà l'intera tabella e raccomanderà le definizioni di colonna per ogni colonna in base ai dati in essa contenuti, ai valori minimi dei campi, ai valori massimi dei campi e così via. A volte, devi solo usare il buon senso con la pianificazione di CHAR vs VARCHAR. Ecco un buon esempio:

Se si memorizzano gli indirizzi IP, la maschera per tale colonna è al massimo di 15 caratteri (xxx.xxx.xxx.xxx). Vorrei saltare subito CHAR(15)in un batter d'occhio perché la lunghezza degli indirizzi IP non varierà molto e la complessità aggiunta della manipolazione delle stringhe controllata da un byte aggiuntivo. Potresti ancora fare un PROCEDURE ANALYSE()contro una colonna del genere. Potrebbe anche raccomandare VARCHAR. In questo caso, i miei soldi sarebbero ancora su CHAR su VARCHAR.

I problemi CHAR vs VARCHAR possono essere risolti solo attraverso un'adeguata pianificazione. Da un grande potere derivano grandi responsabilità (cliché ma vero).

AGGIORNARE

Quando si tratta di MD5, il calcolo di strleninternamente dovrebbe essere eliminato quando si cambia l'intero formato di riga. Non sarebbe necessario modificare la definizione del campo.

Se la chiave MD5 è il solo VARCHAR presente, proverei a farlo e convertire il formato della riga della tabella in fisso . Se è presente un numero significativo di altri campi VARCHAR, anche loro ne trarrebbero beneficio. In cambio, la tabella si espanderebbe a circa il doppio della sua dimensione. Ma le query dovrebbero accelerare di circa il 20% in più senza ulteriore ottimizzazione.


1
Penso che userei un carattere (4) o qualcosa di simile a un numero intero senza segno per un indirizzo IP
Jack Douglas,

@JackPDouglas Hai ragione su quel punto.
RolandoMySQLDBA

Gli indici non sono comunque memorizzati con una lunghezza fissa? Non capisco come cambiando il formato di archiviazione in ricerche a indice a lunghezza fissa migliorate. Vuoi dire che ha migliorato le scansioni della tabella?
Marcus Adams,

1
@JackDouglas, Perché no bite binary?
Pacerier,

@Pacerier sarebbe meglio, sono d'accordo :)
Jack Douglas il

19

Sembra che risparmierai 1 byte per valore o circa il 3% convertendolo in a char. Probabilmente non ne vale la pena se stai comunque memorizzando MD5 in esadecimale - potresti invece risparmiare il 50% usando binaryinvece un .

Grazie a Ovais (vedi commenti) per aver sottolineato che char(32)può utilizzare molto più di 32 byte se si utilizza un set di caratteri multibyte.

Grazie a Rick James per aver sottolineato che dovresti usare la unhexfunzione per convertire una stringa esadecimale in binario:

create table foo(bar varbinary(100));
insert into foo(bar) values(md5('a')); 
insert into foo(bar) values(unhex(md5('a'))); 
select length(bar) from foo;
| lunghezza (bar) |
| ----------: |
| 32 |
| 16 |

db <> violino qui


Buona chiamata al passaggio al binario.
RThomas,

Sto pensando di convertirlo in un file binario. Ora che ci penso, la dimensione non dovrebbe essere diversa solo in base al fatto che sto usando un byte o un carattere poiché la nostra codifica è utf-8. O mi sbaglio?
Jason Baker,

@Jason - la codifica non si applica a binary- o ho capito male?
Jack Douglas,

3
per una colonna char (32) con un set di caratteri di utf-8, ogni valore avrebbe bisogno di 32x3 byte per l'archiviazione. Perché dovresti impostare il valore hash MD5 su utf-8. La conversione in binario (32) richiederebbe 32 byte per valore.
ovais.tariq,

1
Passare a BINARYmolto poco se non si utilizza anche UNHEX(). Cioè, è possibile archiviare UNHEX(MD5(x))in un 16 byte BINARY(16)per risparmiare spazio significativo rispetto alla memorizzazione MD5(x)in CHAR(32) CHARACTER SET ascii.
Rick James,

15

Non vale la pena cambiare secondo me. Se guardi attraverso la documentazione qui dovrebbe illustrare la differenza tra i due. Nel tuo scenario di utilizzo, l'uno non offre realmente alcun vantaggio significativo rispetto all'altro, a meno che tu non sia realmente preoccupato per l'ulteriore sovraccarico legato alle dimensioni della riga.

http://dev.mysql.com/doc/refman/5.0/en/char.html

Nota anche il primo commento sulla documentazione che linko sopra ... "CHAR accelererà il tuo accesso solo se l'intero record è di dimensioni fisse. Cioè, se usi un oggetto di dimensioni variabili, potresti anche farli tutti dimensione variabile. Non ottieni velocità utilizzando un CHAR in una tabella che contiene anche un VARCHAR "


Tale "speedup" si applica a MyISAM, non a InnoDB.
Rick James,
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.