Wie wirkt sich die Verwendung von CHAR im Vergleich zu VARCHAR in einem Feld mit fester Größe auf die Leistung aus?

58

Ich habe eine indizierte Spalte, die einen MD5-Hash speichert. Daher speichert die Spalte immer einen 32-stelligen Wert. Aus welchem ​​Grund auch immer wurde dies eher als ein Varchar als ein Char erstellt. Lohnt es sich, die Datenbank zu migrieren, um sie in ein Zeichen zu konvertieren? Dies ist in MySQL 5.0 mit InnoDB.

Jason Baker
quelle
6
WARNUNG Diese Frage und ihre Antworten wurden geschrieben, bevor InnoDB und utf8 die Standardeinstellungen waren.
Rick James

Antworten:

56

Eine ähnliche Frage wurde zuvor gestellt

Auswirkungen auf die Leistung von MySQL VARCHAR-Größen

Hier ist der Auszug meiner Antwort

Sie müssen die Kompromisse zwischen der Verwendung von CHAR und VARCHAR erkennen

Bei CHAR-Feldern ordnen Sie genau das zu, was Sie erhalten. Beispielsweise weist CHAR (15) 15 Bytes zu und speichert diese, unabhängig davon, wie viele Zeichen Sie in das Feld eingeben. Die Manipulation von Zeichenfolgen ist einfach und unkompliziert, da die Größe des Datenfelds vollständig vorhersehbar ist.

Mit VARCHAR-Feldern erhalten Sie eine ganz andere Geschichte. Beispielsweise weist VARCHAR (15) dynamisch bis zu 16 Bytes, bis zu 15 Bytes für Daten und mindestens 1 zusätzliches Byte zum Speichern der Länge der Daten zu. Wenn Sie die Zeichenfolge 'Hallo' zum Speichern haben, die 6 Bytes und nicht 5 Bytes benötigt, muss bei der Zeichenfolgenmanipulation in jedem Fall eine Art Längenprüfung durchgeführt werden.

Der Kompromiss ist offensichtlicher, wenn Sie zwei Dinge tun: 1. Millionen oder Milliarden von Zeilen speichern 2. Spalten indizieren, die entweder CHAR oder VARCHAR sind

TRADEOFF # 1 VARCHAR hat natürlich den Vorteil, dass Daten mit variabler Länge kleinere Zeilen und damit kleinere physische Dateien erzeugen würden.

TRADEOFF # 2 Da CHAR-Felder aufgrund fester Feldbreiten weniger Zeichenfolgenmanipulation erfordern, sind Index-Lookups für CHAR-Felder im Durchschnitt 20% schneller als für VARCHAR-Felder. Dies ist keine Vermutung von meiner Seite. Das Buch MySQL Database Design and Tuning hat auf einer MyISAM-Tabelle etwas Wunderbares geleistet, um dies zu beweisen. Das Beispiel im Buch hat ungefähr Folgendes bewirkt:

ALTER TABLE tblname ROW_FORMAT=FIXED;

Diese Direktive zwingt alle VARCHARs, sich als CHARs zu verhalten. Ich habe dies bei meinem vorherigen Job im Jahr 2007 getan und eine Tabelle mit 300 GB erstellt und die Indexsuche um 20% beschleunigt, ohne etwas anderes zu ändern. Es funktionierte wie veröffentlicht. Es wurde jedoch ein fast doppelt so großer Tisch erstellt, aber das geht einfach auf Kompromiss Nr. 1 zurück.

Sie können die gespeicherten Daten analysieren, um festzustellen, was MySQL für die Spaltendefinition empfiehlt. Führen Sie einfach Folgendes für eine beliebige Tabelle aus:

SELECT * FROM tblname PROCEDURE ANALYSE();

Dadurch wird die gesamte Tabelle durchsucht und es werden Spaltendefinitionen für jede Spalte basierend auf den darin enthaltenen Daten, den minimalen Feldwerten, den maximalen Feldwerten usw. empfohlen. Manchmal muss man beim Planen von CHAR vs VARCHAR nur den gesunden Menschenverstand anwenden. Hier ist ein gutes Beispiel:

Wenn Sie IP-Adressen speichern, besteht die Maske für eine solche Spalte aus höchstens 15 Zeichen (xxx.xxx.xxx.xxx). Ich würde sofort CHAR(15)weiterspringen, da sich die Länge der IP-Adressen nicht allzu sehr ändert und die zusätzliche Komplexität der String-Manipulation durch ein zusätzliches Byte gesteuert wird. Sie könnten immer noch PROCEDURE ANALYSE()gegen eine solche Kolumne vorgehen. Es kann sogar VARCHAR empfehlen. Mein Geld wäre in diesem Fall immer noch CHAR über VARCHAR.

CHAR vs VARCHAR-Probleme können nur durch ordnungsgemäße Planung gelöst werden. Mit großer Macht geht große Verantwortung einher (Klischee aber wahr).

AKTUALISIEREN

Bei MD5 sollte die strleninterne Berechnung entfallen, wenn das gesamte Zeilenformat umgeschaltet wird. Die Felddefinition müsste nicht geändert werden.

Wenn der MD5-Schlüssel das einzige vorhandene VARCHAR ist, würde ich das Tabellenzeilenformat in ein festes Format konvertieren . Wenn eine signifikante Anzahl anderer VARCHAR-Felder vorhanden ist, würden diese ebenfalls davon profitieren. Im Gegenzug würde sich der Tisch auf etwa die doppelte Größe vergrößern. Abfragen sollten jedoch ohne zusätzliche Optimierung um etwa 20% beschleunigt werden.

RolandoMySQLDBA
quelle
1
Ich denke, ich würde ein Zeichen (4) oder so etwas wie eine vorzeichenlose Ganzzahl für eine IP-Adresse verwenden
Jack Douglas
@JackPDouglas Du bist in diesem Punkt richtig.
RolandoMySQLDBA
Werden Indizes trotzdem mit einer festen Länge gespeichert? Ich verstehe nicht, wie das Ändern des Speicherformats auf feste Länge Index-Lookups verbessert. Meinen Sie damit verbesserte Tischscans?
Marcus Adams
1
@ JackDouglas, warum nicht bitund binary?
Pacerier
@ Pacerier das wäre besser, ich stimme zu :)
Jack Douglas
19

Es sieht so aus, als würden Sie 1 Byte pro Wert einsparen oder etwa 3%, wenn Sie in a konvertieren char. Es lohnt sich wahrscheinlich nicht, wenn Sie MD5 ohnehin hexadezimal speichern - Sie könnten 50% sparen, wenn Sie binarystattdessen a verwenden.

Vielen Dank an Ovais (siehe Kommentare) für den Hinweis, dass char(32)bei Verwendung eines Multibyte-Zeichensatzes viel mehr als 32 Byte verwendet werden können.

Vielen Dank an Rick James, der darauf hingewiesen hat, dass Sie die unhexFunktion verwenden sollten, um eine Hex-Zeichenfolge in eine binäre Zeichenfolge umzuwandeln:

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;
| Länge (bar) |
| ----------: |
| 32 |
| 16 |

db <> hier fummeln

Jack Douglas
quelle
Guter Aufruf zum Umstieg auf Binär.
RThomas
Ich plane, dies in eine Binärdatei umzuwandeln. Nun, da ich darüber nachdenke, sollte die Größe nicht anders sein, nur basierend darauf, ob ich ein Byte oder ein Zeichen verwende, da unsere Codierung utf-8 ist. Oder liege ich falsch?
Jason Baker
@Jason - Codierung trifft nicht zu binary- oder habe ich das falsch verstanden?
Jack Douglas
3
Für eine char (32) -Spalte mit einem Zeichensatz von utf-8 würde jeder Wert 32 x 3 Byte zum Speichern benötigen. Warum müssen Sie den MD5-Hash-Wert auf utf-8 setzen? Die Konvertierung in binär (32) würde 32 Bytes pro Wert erfordern.
ovais.tariq
1
Das Ändern von zu BINARYmacht nur sehr wenig, wenn Sie es auch verwenden UNHEX(). Das heißt, Sie können UNHEX(MD5(x))in einem 16-Byte- BINARY(16)Speicher speichern , um gegenüber dem Speichern MD5(x)in einen erheblichen Speicherplatz einzusparen CHAR(32) CHARACTER SET ascii.
Rick James
15

Es lohnt sich meiner Meinung nach nicht zu ändern. Wenn Sie die Dokumentation hier durchsehen, sollte dies den Unterschied zwischen den beiden veranschaulichen. In Ihrem Nutzungsszenario bietet das eine keinen nennenswerten Vorteil gegenüber dem anderen, es sei denn, Sie sind wirklich besorgt über den zusätzlichen Overhead in Bezug auf die Zeilengröße.

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

Beachten Sie auch den ersten Kommentar zu der Dokumentation, auf die ich oben verweise ... "CHAR beschleunigt den Zugriff nur, wenn der gesamte Datensatz eine feste Größe hat. Wenn Sie also ein Objekt mit variabler Größe verwenden, können Sie auch alle erstellen Variable Größe. Sie gewinnen keine Geschwindigkeit, wenn Sie ein CHAR in einer Tabelle verwenden, die auch ein VARCHAR enthält. "

RThomas
quelle
Diese "Beschleunigung" gilt für MyISAM, nicht für InnoDB.
Rick James