Hat Ihr VARCHAR die richtige Länge?
Autor: Guan Yongqiang, Mitglied des Aikesheng DBA-Teams, verfügt über gute Kenntnisse im Betrieb und in der Wartung von MySQL. Er liebt es, neues Wissen zu erlernen und ist außerdem ein Otaku, der gerne Spiele spielt.
Autor: Li Fuqiang, Mitglied des Aikesheng DBA-Teams, vertraut mit MySQL, TiDB, OceanBase und anderen Datenbanken. Ich glaube, wenn man weiterhin die richtigen Dinge gut macht, wird man anders belohnt.
Produziert von der Aikeson Open Source Community. Bitte kontaktieren Sie den Herausgeber und geben Sie die Quelle für den Nachdruck an.
Dieser Artikel umfasst etwa 2.200 Wörter und die Lektüre dauert voraussichtlich 8 Minuten.
Hintergrundbeschreibung
Einige Kunden berichteten, dass sie die Länge eines Felds vom Typ VARCHAR erweitert haben. Beim ersten Mal können Sie es schnell ändern, beim zweiten Mal dauert die Ausführung jedoch lange. Ich bin verwirrt, weil die Datenmenge in der Tabelle fast gleich ist. Warum ist die VARCHAR(20)
Anpassung von auf schneller, aber die Anpassung von auf dauert lange ?VARCHAR(50)
VARCHAR(50)
VARCHAR(100)
Also haben wir die Situation reproduziert und eine Problemanalyse durchgeführt.
Umweltinformationen
Die an dieser Überprüfung beteiligten Produkt- und Versionsinformationen sind wie folgt:
Produkt | Ausführung |
---|---|
MySQL | 5.7.25-log MySQL Community Server (GPL) |
Sysbench | sysbench 1.0.17 |
Szenenwiederholung
3.1 Datenaufbereitung
mysql> show create table test.sbtest1;
+---------+----------------------------------------+
| Table | Create Table |
+---------+----------------------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`k` int(11) NOT NULL DEFAULT '0',
`c` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
`pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+----------------------------------------+
1 row in set (0.00 sec)
mysql> select count(*) from test.sbtest1;
+----------+
| count(*) |
+----------+
| 1000000 |
+----------+
1 row in set (0.10 sec)
3.2 Problemüberprüfung
Um die Beschreibung des Kunden zu simulieren, c
ändern wir das Feld, VARCHAR(20)
ändern es in VARCHAR(50)
und dann in VARCHAR(100)
und beobachten die für seine Ausführung erforderliche Zeit. Im Folgenden sind die relevanten Betriebsbefehle und Ausführungsergebnisse aufgeführt:
mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(50);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show create table test.sbtest1;
+---------+-------------------------------+
| Table | Create Table |
+---------+-------------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`k` int(11) NOT NULL DEFAULT '0',
`c` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL,
`pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+--------------------------------------------------------+
1 row in set (0.00 sec)
mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(100);
Query OK, 1000000 rows affected (4.80 sec)
Records: 1000000 Duplicates: 0 Warnings: 0
mysql> show create table test.sbtest1;
+---------+---------------------------+
| Table | Create Table |
+---------+---------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`k` int(11) NOT NULL DEFAULT '0',
`c` varchar(100) COLLATE utf8mb4_bin DEFAULT NULL,
`pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+------------------------------------------------------------------------+
1 row in set (0.00 sec)
Bei der Überprüfung wurde festgestellt, dass das Problem weiterhin auftritt, und wir haben weiterhin versucht, es zu ändern. Schließlich haben wir festgestellt, dass die Änderung lange gedauert hat VARCHAR(63)
, VARCHAR(64)
aber nach 64 Jahren haben wir die Länge weiter erweitert und festgestellt, dass dies der Fall ist konnte schnell erledigt werden.
mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(63);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(64);
Query OK, 1000000 rows affected (4.87 sec)
Records: 1000000 Duplicates: 0 Warnings: 0
mysql> show create table test.sbtest1;
+---------+---------------+
| Table | Create Table |
+---------+---------------+
| sbtest1 | CREATE TABLE `sbtest1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`k` int(11) NOT NULL DEFAULT '0',
`c` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL,
`pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(65);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(66);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
3.3 Problemanalyse
VARCHAR(63)
Analysieren Sie die Situation, in der die Änderung lange VARCHAR(64)
dauert . Durch Konsultieren der offiziellen Dokumentation haben wir festgestellt, dass VARCHAR
der Zeichentyp, der Zeichen speichern kann, wenn die Bytelänge 1 beträgt, 0 bis 255 beträgt. Der aktuelle Zeichensatztyp ist UTF8MB4. Da UTF8MB4 ein 4-Byte-codierter Zeichensatz ist, können in einem Byte 63,75 (255/4) Zeichen gespeichert werden. Wenn wir ihnVARCHAR(63)
also ändern, müssen wir ein Byte für Daten hinzufügen VARCHAR(64)
Für die Speicherung ist es notwendig, diese Längenerweiterung durch die Einrichtung einer temporären Tabelle abzuschließen, was viel Zeit in Anspruch nimmt.
Erweiterte Verifizierung
4.1 Datenaufbereitung
mysql> show create table test_utf8.sbtest1;
+---------+----------------------------------------+
| Table | Create Table |
+---------+----------------------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`k` int(11) NOT NULL DEFAULT '0',
`c` varchar(20) NOT NULL DEFAULT '',
`pad` varchar(20) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8 |
+---------+------------------+
1 row in set (0.00 sec)
mysql> select count(*) from test_utf8.sbtest1;
+----------+
| count(*) |
+----------+
| 1000000 |
+----------+
1 row in set (0.10 sec)
4.2 Überprüfung der UTF8-Szene
Da UTF8 ein Drei-Byte-Kodierungszeichensatz ist, kann ein Byte 85 (255/3=85) Zeichen speichern.
Die Reihenfolge dieser Änderung ist: VARCHAR(20)→VARCHAR(50)→VARCHAR(85), und beachten Sie die für ihre Ausführung erforderliche Zeit. Im Folgenden sind die relevanten Operationsbefehle und Ausführungsergebnisse aufgeführt:
mysql> ALTER TABLE test_utf8.sbtest1 MODIFY c VARCHAR(50) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> ALTER TABLE test_utf8.sbtest1 MODIFY c VARCHAR(85) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show create table test_utf8.sbtest1;
+---------+-------------------------------+
| Table | Create Table |
+---------+-------------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`k` int(11) NOT NULL DEFAULT '0',
`c` varchar(85) DEFAULT NULL,
`pad` varchar(20) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8 |
+---------+--------------------------------------------------+
1 row in set (0.00 sec)
Änderungssequenz: VARCHAR(85)→VARCHAR(86)→VARCHAR(100) Zu diesem Zeitpunkt werden wir feststellen, dass die ausgeführte SQL-Anweisung direkt einen Fehler zurückgibt. Daher löschen wir algorithm=inplace ,lock=none
diese beiden Parameter, wodurch diese SQL eine temporäre Tabelle erstellen und die Zieltabelle sperren kann. Anschließend führen wir die SQL erneut aus. Im Folgenden sind die relevanten Operationsbefehle und Ausführungsergebnisse aufgeführt:
mysql> ALTER TABLE test_utf8.sbtest1 MODIFY c VARCHAR(86) ,algorithm=inplace,lock=none;
ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.
mysql> ALTER TABLE test_utf8.sbtest1 MODIFY c VARCHAR(86);
Query OK, 1000000 rows affected (4.94 sec)
Records: 1000000 Duplicates: 0 Warnings: 0
mysql> show create table test_utf8.sbtest1;
+---------+-------------------------------+
| Table | Create Table |
+---------+-------------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`k` int(11) NOT NULL DEFAULT '0',
`c` varchar(86) DEFAULT NULL,
`pad` varchar(20) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8 |
+---------+--------------------------------------------------+
1 row in set (0.00 sec)
mysql> ALTER TABLE test_utf8.sbtest1 MODIFY c VARCHAR(100) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
4.3 Überprüfung des UTF8MB4-Szenarios
Da es sich bei UTF8MB4 um einen Codierungszeichensatz mit vier Bytes handelt, kann ein Byte 63 (255/4 = 63,75) Zeichen speichern.
Die Reihenfolge dieser Änderung ist: VARCHAR(20)→VARCHAR(50)→VARCHAR(63), und beachten Sie die für ihre Ausführung erforderliche Zeit. Im Folgenden sind die relevanten Operationsbefehle und Ausführungsergebnisse aufgeführt:
mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(50) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(63) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show create table test.sbtest1;
+---------+-------------------------+
| Table | Create Table |
+---------+-------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`k` int(11) NOT NULL DEFAULT '0',
`c` varchar(63) COLLATE utf8mb4_bin DEFAULT NULL,
`pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+-------------------------------------------------------------------------+
1 row in set (0.00 sec)
Die Reihenfolge dieser Änderung ist: VARCHAR(63)→VARCHAR(64)→VARCHAR(100). Zu diesem Zeitpunkt werden wir feststellen, dass die ausgeführte SQL-Anweisung direkt einen Fehler zurückgibt. Daher löschen wir algorithm=inplace, lock=none
diese beiden Parameter, wodurch diese SQL eine temporäre Tabelle erstellen und die Zieltabelle sperren kann. Anschließend führen wir die SQL erneut aus. Im Folgenden sind die relevanten Operationsbefehle und Ausführungsergebnisse aufgeführt:
mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(64) ,algorithm=inplace,lock=none;
ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported. Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.
mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(64) ;
Query OK, 1000000 rows affected (4.93 sec)
Records: 1000000 Duplicates: 0 Warnings: 0
mysql> show create table test.sbtest1;
+---------+--------------------------+
| Table | Create Table |
+---------+--------------------------+
| sbtest1 | CREATE TABLE `sbtest1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`k` int(11) NOT NULL DEFAULT '0',
`c` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL,
`pad` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `k_1` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=1000001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+---------+-------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> ALTER TABLE test.sbtest1 MODIFY c VARCHAR(100) ,algorithm=inplace,lock=none;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
4.4 Vergleichende Analyse
Änderung der Zeichenlänge | UTF8(MB3) | UTF8MB4 |
---|---|---|
20->50 | Online-DDL (inplace) | Online-DDL (inplace) |
50->100 | Online-DDL (Kopie) | Online-DDL (Kopie) |
X->Y | Wenn Y*3<256, inplace <br> Wenn X*3>=256, inplace | Wenn Y*4<256, inplace <br> Wenn X*4>=256, inplace |
Anmerkung | Ein Zeichen belegt maximal 3 Bytes | Ein Zeichen belegt maximal 4 Bytes |
abschließend
Wenn die maximale Bytelänge eines Feldes >= 256 Zeichen beträgt, werden 2 Bytes benötigt, um die Feldlänge darzustellen.
Beispiel mit UTF8MB4:
- Für die maximale Bytelänge des Feldes, die innerhalb von 256 Zeichen liegt (d. h. x*4<256 und Y*4<256), verwendet Online-DDL den Inplace-Modus, der sehr effizient ist.
- Für Felder, deren maximale Bytelänge mehr als 256 Zeichen beträgt (d. h. x*4>=256 und Y*4>=256), verwendet Online-DDL den Inplace-Modus, der sehr effizient ist.
- Andernfalls verwendet Online-DDL den Kopiermodus, der ineffizient ist.
- Das Gleiche gilt für UTF8(MB3).
Anregung
Um eine spätere Feldlängenerweiterung zu vermeiden, verwendet Online-DDL den ineffizienten Kopiermodus. Es wird empfohlen, dass:
- Für den Zeichentyp UTF8(MB3):
- Die Anzahl der Zeichen beträgt weniger als 50. Es wird empfohlen, die Zeichenlänge auf VARCHAR(50) oder weniger festzulegen.
- Die Anzahl der Zeichen liegt nahe bei 84 (256/3=83,33). Es wird empfohlen, sie auf varchar(84) oder eine größere Zeichenlänge festzulegen.
- Für den Zeichentyp UTF8MB4:
- Wenn die Anzahl der Zeichen weniger als 50 beträgt, wird empfohlen, sie auf VARCHAR(50) oder eine kleinere Zeichenlänge festzulegen.
- Die Anzahl der Zeichen liegt bei etwa 64 (256/4=64). Es wird empfohlen, sie auf VARCHAR(64) oder eine größere Zeichenlänge festzulegen.
Die Ergebnisse dieser Überprüfung dienen nur als Referenz. Wenn Sie in einer Produktionsumgebung arbeiten müssen, definieren Sie bitte die Länge von VARCHAR basierend auf der tatsächlichen Situation, um wirtschaftliche Verluste zu vermeiden.
Weitere technische Artikel finden Sie unter: https://opensource.actionsky.com/
Über SQLE
SQLE ist eine umfassende SQL-Qualitätsmanagementplattform, die die SQL-Prüfung und -Verwaltung von der Entwicklung bis zur Produktionsumgebung abdeckt. Es unterstützt gängige Open-Source-, kommerzielle und inländische Datenbanken, bietet Prozessautomatisierungsfunktionen für Entwicklung, Betrieb und Wartung, verbessert die Online-Effizienz und verbessert die Datenqualität.
SQLE erhalten
Typ | Adresse |
---|---|
Repository | https://github.com/actiontech/sqle |
dokumentieren | https://actiontech.github.io/sqle-docs/ |
Neuigkeiten veröffentlichen | https://github.com/actiontech/sqle/releases |
Entwicklungsdokumentation für das Datenaudit-Plug-in | https://actiontech.github.io/sqle-docs/docs/dev-manual/plugins/howtouse |