mysqlが変更した文字セットutf8mb4によって引き起こされた悲劇

mysqlが変更した文字セットutf8mb4によって引き起こされた悲劇

  • 環境構成:Linux CentOS 7mysql5.7文字エンコードはutf8です。
  • 悲劇の原因:データベーステーブルは絵文字をサポートする必要があります。絵文字は通常4文字です。utf8は最大3文字をサポートします。4文字のフィールドに絵文字を挿入すると、エラーが報告されます。そのため、文字を変更しました。このテーブルのセットをutf8mb4に設定します。ここでは、utf8mb4がutf8のスーパーセットであることを説明します。
  • ここで問題が発生します。MySQL環境には左結合で使用されるフィールドにインデックスが付けられた2つのテーブルがありますが、実行プランでは、1つのテーブルが全表スキャンを使用してテーブル全体のほぼ100万行のレコードをスキャンすることが示されています。 、SQLの実行が遅くなります。
  • 診断結果:mysqlテーブルの文字セットutf8mb4の変更によって引き起こされた悲劇。
  • 問題診断が再現されます。まず、テーブル構造とテーブルレコードは次のとおりです。
CREATE TABLE `t1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`code` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_code` (`code`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8

t1いくつかのデータを挿入します

CREATE TABLE `t2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`code` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_code` (`code`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4

T2はいくつかのデータを挿入し

、2つのテーブルの左結合の実行プランは次のとおりです。

desc select * from t2 join t1 on t1.code = t2.code where t2.name = 'dddd'\G;

  • t2.name = 'dddd'がインデックスを使用し、関連付け条件t1.code = t2.codeがt1.codeのインデックスを使用しないことがはっきりとわかります。最初は戸惑いましたが、マシンは使用しませんでした。だまします。警告を表示し
    て、クエリ実行プランの警告情報を
    表示します。showwarnings;

  • 問題を発見:変換を発見した後(utf8mb4のtestdb.t1.codeを使用)、スコットは2つのテーブルの文字セットが異なることを発見しました。t1はutf8、t2はutf8mb4です。しかし、テーブルとテーブルの文字セットが異なるのはなぜですか(実際には文字セットを変更しましたが、文字セットが同じではありません)、T1全表スキャンが発生しますか?以下で分析してみましょう。
    (1)まず、t2がt1を左結合して、t2が駆動テーブルであることを確認します。この手順は、t2から選択*を実行するのと同じです。ここで、t2.name = 'dddd'で、コード範囲の値を取得します。 ';
    (2)次に、t2で見つかったコードの値を取得し、接続条件に従ってt1で検索します。この手順は、select * from t1 where t1.code =' 8a77a32a7e0825f7c8634226105c42e5 ';
    (3)を実行するのと同じです。ステップ(1)のT2テーブルの抽出されたコードフィールドはutf8mb4文字セットであり、T1テーブルのコードはUTF8文字セットです。ここでは文字セット変換が必要です。文字セット変換は、 utf8mb4はUTF8のスーパーセットであるため、小さいものから大きいものへ。UTF8はutf8mb4に変換します。つまり、t1.codeをutf8mb4文字セットに変換します。変換後、t1.codeのインデックスはまだUTF8文字セットであるため、このインデックスは無視されます。実行計画によって、T1テーブルは完全なテーブルスキャンのみを選択できます。さらに悪いことに、T2が複数のレコードを除外すると、T1はテーブル全体で複数回スキャンされ、パフォーマンスの違いが想像できます。

  • 問題を解決する:
    原因が明らかになったので、どのように解決しますか?もちろん、文字セットを変更することです。T1をT2と同じに変更することも、T2をT1に変更することもできます。ここでは、T1をutf8mb4に変更することを選択します。文字セットを変換する方法は?
    学生は、alter table t1 charset utf8mb4を使用すると言いますが、これは間違っています。これは、テーブルの置換文字セットのみが変更されるためです。つまり、新しいデータはutf8mb4を使用し、既存の部分はまだutf8です。
    テーブルt1を文字セットutf8mb4に変換するだけです;正しいです。
    ただし、テーブルを変更して文字セットを変更する操作は二重に記述されているため(ロック=ノードはエラーを報告します)、ピーク時は操作しないでください。ビジネスでは、pt-online -schemaを使用して大きなテーブルを操作することをお勧めしますが、オンライン変更の文字セットが変更されます。
    テスト環境:alter table t1を使用してcharsetutf8mb4に変換します。lock= shared;

実行プランをもう一度見てみると、問題がないことがわかります。

  • 再検出の概要:
    1。テーブルの文字セットが異なる場合、追加されたSQLはインデックスを使用せず、重大なパフォーマンスの問題を引き起こす可能性があります
    。2。文字セットを変更するテーブルの変更操作は複数回書き込まれます。Businessmysqlは次の使用を推奨します。 pt-online-schema-change;
    3.テーブルの文字セットを大量に変更する場合は、SQLレビューでも同じことを行い、関連するテーブルの文字セットを一緒に変更します。
    4.例に従って、showwarningsを使用します[忘れないでください]

おすすめ

転載: blog.csdn.net/qq_31555951/article/details/106615110