MySQL VARCHAR の最適な長さの評価方法

VARCHAR の長さは適切ですか?

著者: Aikesheng DBA チームのメンバーである Guan Yongqiang は、MySQL の運用とメンテナンスのスキルに優れています。新しい知識を学ぶのが大好きで、ゲームも大好きなオタクです。

著者: Li Fuqiang 氏、Aikesheng DBA チームのメンバーで、MySQL、TiDB、OceanBase およびその他のデータベースに精通しています。正しいことをしっかりやり続ければ、また違ったご褒美が得られると信じています。

Aikeson オープンソース コミュニティによって作成されています。オリジナルのコンテンツを許可なく使用することはできません。転載する場合は編集者に連絡し、出典を明記してください。

この記事は約 2,200 ワードで、読むのに 8 分かかると予想されます。

背景の説明

一部の顧客は、 VARCHAR型フィールドの長さを拡張したと報告しました。初回はすぐに変更できますが、2 回目は実行に時間がかかります。テーブル内のデータ量はほぼ同じなので、から に調整するのは速いのに、から に調整するのに時間がかかるのはなぜですか?VARCHAR(20)VARCHAR(50)VARCHAR(50)VARCHAR(100)そこで状況を再現し、問題分析を実施しました。

環境情報

今回の検証に対象となる製品とバージョン情報は以下のとおりです。

製品 バージョン
MySQL 5.7.25-ログ MySQL コミュニティ サーバー (GPL)
システムベンチ システムベンチ 1.0.17

シーンの繰り返し

3.1 データの準備

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 問題点の検証

顧客の説明をシミュレートするために、cフィールドを変更し、VARCHAR(20)に変更しVARCHAR(50)、次に に変更しVARCHAR(100)、その実行に必要な時間を観察します。関連する操作コマンドと実行結果は次のとおりです。

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)

VARCHAR(63)検証の結果、安定して再発することがわかったので、引き続き修正を試みましたが、最終的にに修正するのに時間がかかることが判明しましたVARCHAR(64)が、64以降も長さを拡張し続けたところ、 になることがわかりました。すぐに完了することができました。

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 問題分析

VARCHAR(63)修正VARCHAR(64)時間がかかる状況を分析します。公式ドキュメントを参照すると、VARCHARバイト長が1の場合に格納できる文字の種類は0~255であることがわかりました。現在の文字セット タイプは UTF8MB4 です。UTF8MB4 は 4 バイトのエンコード文字セットであるため、1 バイト長で 63.75 (255/4) 文字を格納できるため、これを にVARCHAR(63)変更する場合は、データ用のバイトを追加する必要があります。VARCHAR(64)ストレージの場合は、一時テーブルを作成してこの長さの拡張を完了する必要があるため、時間がかかります。

拡張検証

4.1 データの準備

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 UTF8シーン検証

UTF8 は 3 バイトのエンコーディング文字セットであるため、1 バイトに 85 (255/3=85) 文字を格納できます。

この変更の順​​序は、VARCHAR(20)→VARCHAR(50)→VARCHAR(85)となり、その実行にかかる時間を観察します。関連する操作コマンドと実行結果は次のとおりです。

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)

変更順序: VARCHAR(85)→VARCHAR(86)→VARCHAR(100) このとき、実行された SQL ステートメントが直接エラーを返すことがわかります。したがって、これら 2 つのパラメータを削除するとalgorithm=inplace ,lock=none、この SQL で一時テーブルを作成し、ターゲット テーブルをロックしてから、SQL を再実行できるようになります。関連する操作コマンドと実行結果は次のとおりです。

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 UTF8MB4シナリオの検証

UTF8MB4 は 4 バイトのエンコーディング文字セットであるため、1 バイトに 63 (255/4=63.75) 文字を格納できます。

この変更の順​​序は、VARCHAR(20)→VARCHAR(50)→VARCHAR(63)となり、その実行にかかる時間を観察します。関連する操作コマンドと実行結果は次のとおりです。

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)

この変更の順​​序は、VARCHAR(63)→VARCHAR(64)→VARCHAR(100) になります。このとき、実行された SQL ステートメントが直接エラーを返すことがわかります。したがって、これら 2 つのパラメータを削除するとalgorithm=inplace, lock=none、この SQL で一時テーブルを作成し、ターゲット テーブルをロックしてから、SQL を再実行できるようになります。関連する操作コマンドと実行結果は次のとおりです。

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 比較分析

文字長変更 UTF8(MB3) UTF8MB4
20->50 オンライン DDL (インプレース) オンライン DDL (インプレース)
50->100 オンライン DDL (コピー) オンライン DDL (コピー)
X->Y Y*3<256の場合、インプレース <br> X*3>=256の場合、インプレース Y*4<256の場合、インプレース <br> X*4>=256の場合、インプレース
述べる 1文字は最大3バイトを占有します 1文字は最大4バイトを占有します

結論は

フィールドの最大バイト長が 256 文字以上の場合、フィールド長を表すために 2 バイトが必要です。

UTF8MB4を使用した例:

  • フィールドの最大バイト長が 256 文字以内に変化する場合 (つまり、x*4<256 および Y*4<256)、オンライン ddl は非常に効率的なインプレース モードを使用します。
  • 最大バイト長が 256 文字を超えるフィールド (つまり、x*4>=256 および Y*4>=256) の場合、オンライン DDL は非常に効率的なインプレース モードを使用します。
  • それ以外の場合、オンライン DDL はコピー モードを使用するため、非効率的になります。
  • UTF8(MB3)でも同様です。

提案

後のフィールド長の拡張を避けるために、オンライン DDL は非効率的なコピー モードを採用することをお勧めします。

  • UTF8(MB3)文字タイプの場合:
    • 文字数が50未満です。文字長はVARCHAR(50)以下に設定することを推奨します。
    • 文字数は 84 (256/3=83.33) に近い値です。varchar(84) またはそれ以上の文字数に設定することをお勧めします。
  • UTF8MB4 文字タイプの場合:
    • 文字数が 50 未満の場合は、VARCHAR(50) またはそれより短い文字長に設定することをお勧めします。
    • 文字数は 64 に近い (256/4=64)、VARCHAR(64) 以上の文字数に設定することをお勧めします。

この検証結果は参考用です。本番環境で運用する必要がある場合は、経済的損失を避けるために、実際の状況に基づいて VARCHAR の長さを合理的に定義してください。

さらに技術的な記事については、https: //opensource.actionsky.com/をご覧ください。

SQLEについて

SQLE は、開発環境から運用環境までの SQL 監査と管理をカバーする包括的な SQL 品質管理プラットフォームです。主流のオープンソース、商用および国内データベースをサポートし、開発、運用および保守のためのプロセス自動化機能を提供し、オンライン効率を向上させ、データ品質を向上させます。

SQL取得

タイプ 住所
リポジトリ https://github.com/actiontech/sqle
書類 https://actiontech.github.io/sqle-docs/
リリースニュース https://github.com/actiontech/sqle/releases
データ監査プラグイン開発ドキュメント https://actiontech.github.io/sqle-docs/docs/dev-manual/plugins/howtouse
1990 年代生まれのプログラマーがビデオ移植ソフトウェアを開発し、1 年足らずで 700 万以上の利益を上げました。結末は非常に罰的でした。 高校生が成人式にオープンソースプログラミング言語を自作―ネチズンの鋭いコメント: 詐欺横行でRustDesk依存、国内サービスの タオバオ(taobao.com)は国内サービスを一時停止、ウェブ版の最適化作業を再開 Java最も一般的に使用されている Java LTS バージョンは 17 、Windows 11 は減少し続ける Open Source Daily | Google がオープンソースの Rabbit R1 を支持、Microsoft の不安と野心; Electricがオープンプラットフォームを閉鎖 AppleがM4チップをリリース GoogleがAndroidユニバーサルカーネル(ACK)を削除 RISC-Vアーキテクチャのサポート Yunfengがアリババを辞任し、将来的にはWindowsプラットフォームで独立したゲームを制作する予定
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/actiontechoss/blog/11094958