Best Practices für die Längenauswertung von MySQL VARCHAR

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 VARCHARder 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=nonediese 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=nonediese 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
Ein in den 1990er Jahren geborener Programmierer hat eine Videoportierungssoftware entwickelt und in weniger als einem Jahr über 7 Millionen verdient. Das Ende war sehr bestrafend! High-School-Schüler erstellen im Rahmen einer Coming-of-Age-Zeremonie ihre eigene Open-Source-Programmiersprache – scharfe Kommentare von Internetnutzern: Der inländische Dienst Taobao (taobao.com) verließ sich aufgrund des grassierenden Betrugs auf RustDesk und stellte die inländischen Dienste ein und startete die Arbeit zur Optimierung der Webversion von Java neu 17 ist die am häufigsten verwendete Java LTS-Version. Windows 11 erreicht weiterhin einen Rückgang. Open Source Daily unterstützt die Übernahme von Open Source Rabbit R1; Electric schließt die offene Plattform Apple veröffentlicht M4-Chip Google löscht Android Universal Kernel (ACK) Unterstützung für RISC-V-Architektur Yunfeng ist von Alibaba zurückgetreten und plant, in Zukunft unabhängige Spiele auf der Windows-Plattform zu produzieren
{{o.name}}
{{m.name}}

Ich denke du magst

Origin my.oschina.net/actiontechoss/blog/11094958
Empfohlen
Rangfolge