二、【mysql】解説と実戦解説

目次

この章の内容は、mysql バージョン 5.7 に基づいて説明されています。

1. 理解する 説明する

1.導​​入の説明

EXPLAIN ステートメントは、MySQL がステートメントを実行する方法に関する情報を提供します。EXPLAIN は、SELECT、DELETE、INSERT、REPLACE、および UPDATE ステートメントに適用されます。

EXPLAIN キーワードを使用して、SQL ステートメントを実行するオプティマイザをシミュレートし、クエリ ステートメントまたは構造のパフォーマンスのボトルネックを分析し、select ステートメントの前に Explain キーワードを追加します。MySQL はクエリにマークを設定し、クエリを実行して実行結果を返します。この SQL を実行する代わりに、計画情報を取得します。
注: from にサブクエリが含まれている場合でも、サブクエリは実行され、結果は一時テーブルに配置されます。

公式ドキュメント


2. 関連分野の説明

まず、次のような 3 つのテーブルが作成されました

 CREATE TABLE `actor` (
 `id` INT(11) NOT NULL,
 `name` VARCHAR(45) DEFAULT NULL,
 `update_time` DATETIME DEFAULT NULL,
 PRIMARY KEY (`id`)
 ) ENGINE=INNODB DEFAULT CHARSET=utf8;

これらのテーブルにいくつかのデータを自分で挿入するだけです。
ここに画像の説明を挿入します

ムービーテーブル (単一値インデックスを追加)

CREATE TABLE `film` (
 `id` INT(11) NOT NULL AUTO_INCREMENT,
 `name` VARCHAR(10) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `idx_name` (`name`)
 ) ENGINE=INNODB DEFAULT CHARSET=utf8;

俳優映画関連テーブル (共同インデックスを追加)

CREATE TABLE `film_actor` (
  `id` INT(11) NOT NULL,
  `film_id` INT(11) NOT NULL COMMENT '电影表主键',
  `actor_id` INT(11) NOT NULL COMMENT '演员表主键',
  `remark` VARCHAR(255) DEFAULT NULL COMMENT '额外增加的字段',
  PRIMARY KEY (`id`),
  KEY `idx_film_actor_id` (`film_id`,`actor_id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

1.パーティション

バージョン 5.7 には Explain を実行するときにデフォルトでこの列がありますが、以前のバージョンにはこの列がない可能性があるため、PARTITIONS キーワードを追加する必要があります。
テーブルにパーティションがあるかどうかですが、ほとんどの場合は使用されませんが、使用されている場合はパーティション分割せずにデータベースとテーブルに分割されます。パーティションがない場合は NULL になります。このフィールドは無視できます。

旧版本:EXPLAIN PARTITIONS SELECT * FROM `actor` WHERE id = 1;

5.7之后版本:EXPLAIN  SELECT * FROM `actor` WHERE id = 1;

ここに画像の説明を挿入します

2.フィルター済み

バージョン 5.7 には Explain 実行時にデフォルトでこの列がありますが、以前のバージョンにはこの列がない可能性があるため、EXTENDED キーワードを追加する必要があります。

旧版本:EXPLAIN EXTENDED SELECT * FROM `actor` WHERE id = 1;

5.7之后版本:EXPLAIN  SELECT * FROM `actor` WHERE id = 1;

フィルタリングされた列は半分の比率の値です。 rows *filtered/100 は、Explain 内の前のテーブルに接続される行数を推定できます (前のテーブルとは、Explain 内の ID 値が現在のテーブル ID 値)。

3.警告表示コマンド

EXPLAIN   SELECT * FROM `actor` WHERE id = 1;
SHOW WARNINGS;

ここに画像の説明を挿入します
SHOW WARNINGS の実行結果。
ここに画像の説明を挿入します

select '1' AS `id`,'Li' AS `name`,'2023-09-04 19:02:55' AS `update_time` from `org_47`.`actor` where 1

結果 2 は、mysql による SQL ステートメントの最適化の結果です。この結果は直接実行できない可能性があります。

3. より重要なフィールドの説明

1.id

id欄の数字はセレクトの通し番号で、セレクトが複数あるためidも複数あり、セレクトが出現した順にidの順番が増えます。
id列が大きいほど実行優先度が高く、idが同じ場合は上から順に実行され、idがNULLの場合は最後に実行されます。

2.タイプの選択

対応する行が単純なクエリであるか複雑なクエリであるかを示します。

  1. シンプル: 単純なクエリ。クエリにサブクエリとユニオンが含まれていません
  2. プライマリ: 複雑なクエリの最も外側の選択
  3. subquery: select に含まれるサブクエリ (from 句には含まれません)
  4. 派生: from 句に含まれるサブクエリ。MySQL は結果を一時テーブルに保存します。このテーブルは派生テーブル (派生テーブルの英語の意味) とも呼ばれます。

=》SQLの例=》

単純なクエリ

EXPLAIN SELECT * FROM actor WHERE id = 1

ここに画像の説明を挿入します
DERIVED を含むクエリの場合、
派生テーブルのマージを最適化するために mysql5.7 の新機能をオフにする必要があります。
閉店前

EXPLAIN SELECT (SELECT 1 FROM actor WHERE id = 1) FROM (SELECT * FROM film WHERE id = 1) der

ここに画像の説明を挿入します
mysql5.7 の新機能をオフにして、派生テーブルのマージを最適化します。

SET SESSION optimizer_switch='derived_merge=off';
EXPLAIN SELECT (SELECT 1 FROM actor WHERE id = 1) FROM (SELECT * FROM film WHERE id = 1) der

ここに画像の説明を挿入します
idが大きいほど最初に実行され、id値が等しい場合は行順に実行されます。
この例と 3.table を組み合わせると、mysql が最初に film テーブルのデータをクエリし、次に Actor テーブルをサブクエリし、最後に派生テーブル (一時テーブル) をクエリしていることがわかります。

3.テーブル

この列は、Explain の行によってどのテーブルがアクセスされているかを示します。
from 句にサブクエリがある場合、テーブルの列は、現在のクエリが id=N のクエリに依存していることを示す形式になるため、 id=N のクエリが最初に実行されます
ユニオンがある場合、UNION RESULT のテーブル列の値は <union1,2> で、1 と 2 はユニオンに参加している選択行 ID を表します。
たとえば、上の例では、3 は ID の 3 に依存します。
ここに画像の説明を挿入します

4.タイプ

この列は関連付けタイプまたはアクセス タイプを表します。つまり、MySQL はテーブル内の行の検索方法とデータ行レコードのおおよその範囲を決定します。
最良から最悪の順に、system > const > eq_ref > ref > range > index > ALL
一般に、クエリが範囲レベル (できれば ref) に到達することを確認する必要があります。

  • NULL : MySQL は最適化フェーズ中にクエリ ステートメントを分解でき、実行フェーズ中にテーブルやインデックスにアクセスする必要はありません。たとえば、インデックス列の最小値の選択は、
    実行中にテーブルにアクセスせずにインデックスのみを検索することで実行できます。(この状況は比較的まれです)
    =》sql の例=》
EXPLAIN SELECT MIN(id) FROM film;

ここに画像の説明を挿入します
この例では、テーブルの列は NULL です。これは、クエリ ステートメントが最適化フェーズ中に分解され、実行フェーズ中にテーブルまたはインデックスにアクセスせずに結果を取得できることを意味します。

  • const, system : MySQL はクエリの特定の部分を最適化し、それを定数に変換できます (show warnings の結果を参照)。主キーまたは一意キーのすべての列が定数と比較される場合
    、テーブルには一致する行が 1 つだけあり、読み取られるのは 1 回なので高速です。System は
    const の特殊なケースで、テーブル内に一致するタプルが 1 つだけある場合、それは system です。
    =》SQLの例=》
EXPLAIN  SELECT * FROM (SELECT * FROM film WHERE id = 1) tmp;
SHOW WARNINGS;

ここに画像の説明を挿入します
ここに画像の説明を挿入します

/* select#1 */ SELECT '1' AS `id`,'电影1' AS `name` FROM DUAL
  • eq_ref : 主キーまたは一意キー インデックスのすべての部分が接続で使用され、条件を満たすレコードが最大 1 つだけ返されます。これは const 以外に最適な接続タイプである可能性があります
    。このタイプは単純な選択クエリには表示されません
    =》sql example=》
EXPLAIN SELECT * FROM film_actor LEFT JOIN film ON film_actor.film_id = film.id

ここに画像の説明を挿入します

関連するクエリを実行するときは、両方のテーブルのデータをクエリする必要があるため、ID 値は同じであり、最初の行が最初に実行され、次に 2 番目の行が実行されます。

  • ref : eq_ref と比較して、一意のインデックスを使用せず、通常のインデックス (セカンダリ インデックス) または一意のインデックスの一部のプレフィックスを使用します。インデックスは特定の値と比較する必要があり、複数の条件を満たす行が見つかる可能性があります。
    =》SQL 例 1=》
EXPLAIN SELECT * FROM film WHERE NAME = 'film1';

ここに画像の説明を挿入します
この SQL は、このテーブルのセカンダリ インデックスを使用します。 idx_name
ここに画像の説明を挿入します
= "sql example 2 = "
関連するテーブル クエリ、idx_film_actor_id は、film_id とactor_id の結合インデックスであり、film_actor の左側のプレフィックス film_id 部分がここで使用されます。

EXPLAIN SELECT film_id FROM film LEFT JOIN film_actor ON film.id = film_actor.film_id

ここに画像の説明を挿入します
ここに画像の説明を挿入します

  • range : 範囲スキャンは通常、in()、between、>、<、>= およびその他の操作で発生します。インデックスを使用して、指定された範囲の行を取得します。
    =》SQLの例=》
EXPLAIN SELECT * FROM actor WHERE id > 1;

ここに画像の説明を挿入します

  • Index : インデックス全体をスキャンすることで結果を取得できます。通常、セカンダリ インデックスがスキャンされます。このスキャンは、インデックス ツリーのルート ノードから開始して迅速に検索するのではなく、セカンダリ インデックスのリーフ ノードを直接走査してスキャンします。速度は同じです。比較的低速ですが、この種のクエリでは通常、カバリング インデックスが使用され、セカンダリ インデックスが一般に小さいため、この種のクエリは通常、ALL より高速です。

ここでインデックスのカバーについて話しましょう
。id、name、性別の 3 つの列を持つテーブル user_info があるとします。単一値インデックスを追加します: KEY idx_name (name)

CREATE TABLE `user_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL COMMENT '姓名',
  `sex` varchar(2) DEFAULT NULL COMMENT '性别',
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

テーブルデータ:
ここに画像の説明を挿入します

最初のブログで述べたように、主キー インデックスとセカンダリ インデックスに対応する B+ ツリー データ構造は次のようになります。

主キーインデックス:
ここに画像の説明を挿入します

セカンダリインデックス:
ここに画像の説明を挿入します
SQL ステートメント:

EXPLAIN SELECT id,NAME FROM `user_info`

ここに画像の説明を挿入します
名前列にインデックスがある場合: 名前と ID のみがクエリされます。セカンダリ インデックスのデータ構造では、リーフ ノードがクエリの内容を完全に満たしており、主キー インデックスと比較して、主キー インデックスのリーフ ノードにはより多くのデータが含まれていることがわかります (データをクエリする場合、データがメモリにロードされ、プライマリ キー インデックスがより多くのスペースを占有し、セカンダリ データが占有するスペースが少なくなります)。したがって、mysql はセカンダリ インデックスでクエリを実行することを選択します。セカンダリ インデックスは、クエリ対象の列の内容をカバーします。この条件は、セカンダリ インデックスをクエリすることによって満たされます。つまり、インデックスをカバーします。カバリングインデックスはインデックスではなく、方法です。

  • ALL: : つまり、クラスター化インデックスのすべてのリーフ ノードをスキャンするフル テーブル スキャンです。通常、これには最適化のためにインデックスを追加する必要があります。

5.可能なキー

この列には、クエリが検索に使用できるインデックスが表示されます。
Explain では、 possible_keys カラムにコンテンツがあるにもかかわらず、キーに NULL が表示される状況が発生することがあります。これは、テーブルにデータがあまりなく、MySQL はインデックスがこのクエリにはあまり役に立たないと考えているためです
。完全なテーブルクエリ。
列が NULL の場合、関連付けられたインデックスはありません。この場合、where 句をチェックして適切なインデックスを作成してクエリのパフォーマンスを向上できるかどうかを確認し
、explain を使用してその効果を確認できます。
上記の例を組み合わせて理解してください。

6.キー

この列は、mysql がテーブルへのアクセスを最適化するために実際に使用するインデックスを示します。
インデックスが使用されていない場合、この列は NULL になります。MySQL に possible_keys 列のインデックスの使用または無視を強制する場合は、クエリで強制
インデックスまたは無視インデックスを使用します。
上記のいくつかの例を理解してください。

7.key_len

この列には、インデックス内の MySQL によって使用されるバイト数が表示されます。この値は、インデックス内のどの列が使用されているかを計算するために使用できます。
=》SQLの例=》

EXPLAIN SELECT * FROM film_actor WHERE film_id = 999;

ここに画像の説明を挿入します
キー列から、このクエリが idx_film_actor_id インデックスを使用していることがわかります。これは 2 つの列を持つ結合インデックス (film_id、actor_id) です。
ここに画像の説明を挿入します
どちらも int 型で、1 つの int は 4 バイトを占有し、2 つの int は 4+4=8 バイトを占有します。
key_len 列が 4 であることから、このクエリはインデックスの一部、つまり film_id に関連するインデックスのみを使用していることがわかります。前のブログの左端の原則を組み合わせると、ジョイント インデックスの優先順位が最初に film_id、次に Actor_id によって並べ替えられることを知ることは難しくありません。ジョイント インデックス内の Actor_id をクエリする場合は、film_id が順序どおりであることを確認する必要があります。 、actor_id なので、film_id に基づいて順序付けされます。ken_len が分からない場合は、一番左の原理から推測することもできますが、ken_len があった方が便利です。

EXPLAIN SELECT * FROM film_actor WHERE film_id = 999 AND actor_id = 666;

ここに画像の説明を挿入します
この例は、すべてのフィールドにインデックスが付けられていることを意味します。

key_len の計算ルールは次のとおりです。

  1. 文字列、char(n) および varchar(n)。バージョン 5.0.3 以降では、n はバイト数ではなく文字数を表します。UTF-8 の場合、1 つの数字または文字は 1 バイトを占め、もう 1 つはバイトを占めます。中国語の文字は 3 バイトを占めます
    (1) char (n): 中国語の文字が格納されている場合、長さは 3n バイトです。
    (2) varchar (n): 中国語の文字が格納されている場合、長さは 3n + 2 バイトです。追加の 2 バイトは文字を格納するために使用されます。 varchar は可変長文字列であるため、文字列の長さ
    (3) varchar は可変長文字列です。
  2. 数値型
    (1) tinyint: 1 バイト
    (2) smallint: 2 バイト
    (3) int: 4 バイト
    (4) bigint: 8 バイト
  3. 時刻型
    (1) 日付: 3 バイト
    (2) タイムスタンプ: 4 バイト
    (3) 日時: 8 バイト

フィールドが NULL を許可する場合、NULL かどうかを記録するために 1 バイトが必要です。
索引最大长度是768字节,当字符串过长时,mysql会做一个类似左前缀索引的处理,将前半部分的字符提取出来做索引。

8.ref

この列には、キー列レコードのインデックス内の値を検索するためにテーブルで使用される列または定数が表示されます。一般的なものは、const (定数)、フィールド名 (テーブル名。列名) です。

EXPLAIN SELECT film_id FROM film LEFT JOIN film_actor ON film.id = film_actor.film_id;

ここに画像の説明を挿入します

9.行

この列は、 mysql估计が読み取って検出する行数です。これは結果セット内の行数ではないことに注意してください。

10.エクストラ

この列には追加情報が表示されます。一般的に重要な値は次のとおりです。

  • インデックスの使用: カバリングインデックスを使用します。
EXPLAIN SELECT film_id FROM film_actor WHERE film_id = 1;

ここに画像の説明を挿入します

  • where の使用: where ステートメントを使用して結果を処理します。クエリされた列はインデックスの対象になりません。
EXPLAIN SELECT * FROM actor WHERE NAME = 'qqqq';

ここに画像の説明を挿入します

  • インデックス条件の使用: クエリされた列はインデックスによって完全にカバーされておらず、where 条件は先頭の列の範囲です。
EXPLAIN SELECT * FROM film_actor WHERE film_id > 1;

ここに画像の説明を挿入します

  • 一時的な使用: mysql はクエリを処理するために一時テーブルを作成する必要があります。このような場合は、通常、インデックスを使用して最適化を行う必要があります。
EXPLAIN SELECT DISTINCT NAME FROM actor

ここに画像の説明を挿入します
この問題は、変更された名前にインデックスを追加することで解決できます。
前述の例では、フィルム テーブル内の名前にインデックスが付けられます。

EXPLAIN SELECT DISTINCT NAME FROM film

ここに画像の説明を挿入します

  • ファイルソートの使用: インデックスソートの代わりに外部ソートが使用されます。データが小さい場合はメモリ内でソートされ、それ以外の場合はディスク上でソートされます。この場合、通常はインデックスを使用した最適化を検討する必要があります。(詳しくは後ほど説明します)
EXPLAIN SELECT * FROM actor ORDER BY NAME;

ここに画像の説明を挿入します

EXPLAIN SELECT * FROM film ORDER BY NAME;

ここに画像の説明を挿入します

  • 最適化されたテーブルを選択する: 特定の集計関数 (max、min など) を使用してインデックスのあるフィールドにアクセスする場合。

4. 実践的なインデックス最適化 (原則に従う)

テーブルを作成する

CREATE TABLE `employees` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',
  `age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
  `position` varchar(20) NOT NULL DEFAULT '' COMMENT '职位',
  `hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间',
  PRIMARY KEY (`id`),
  KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='员工记录表'
INSERT INTO employees(NAME,age,POSITION,hire_time) VALUES('LiHua',22,'manager',NOW());
INSERT INTO employees(NAME,age,POSITION,hire_time) VALUES('HanLei',23,'dev',NOW());
INSERT INTO employees(NAME,age,POSITION,hire_time) VALUES('Lucy',23,'dev',NOW());

1. 完全値一致

条件内のフィールドは正確に一致する必要があります

=》SQLの例=》

EXPLAIN SELECT * FROM employees WHERE NAME= 'LiLei';

ここに画像の説明を挿入します
この key_len はどこから来たのでしょうか: 名前は varchar (24) です。上記の式により、3n+2 が計算されます: 3*24+2=74

EXPLAIN SELECT * FROM employees WHERE LEFT(NAME,3)= 'LiLei';

ここに画像の説明を挿入します
left 関数がわからない場合は、この関数は Left (str, len) という 2 つのパラメーターを受け取ります。str はインターセプトする文字列、len は左側からインターセプトする桁数です。

SELECT LEFT('LiLei',3) AS 结果

ここに画像の説明を挿入します

2. 左端のプレフィックスルール

複数の列にインデックスが付けられている場合は、一番左のプレフィックス ルールに従う必要があります。これは、クエリがインデックスの左端の列から開始され、インデックス内の列をスキップしないことを意味します。
内容については以前に詳しく説明したので、ここでは例のみを示します。

EXPLAIN SELECT * FROM employees WHERE NAME = 'Bill' AND age = 31;

ここに画像の説明を挿入します

EXPLAIN SELECT * FROM employees WHERE age = 30 AND POSITION = 'dev';

ここに画像の説明を挿入します

EXPLAIN SELECT * FROM employees WHERE POSITION = 'manager';

ここに画像の説明を挿入します

3. インデックス列に対して操作 (計算、関数、(自動または手動) 型変換) を実行しないと、インデックスが失敗し、テーブル全体のスキャンが行われます。

=》sql example=《
インデックスを追加

ALTER TABLE `employees` ADD INDEX `idx_hire_time` (`hire_time`) USING BTREE ;
EXPLAIN select * from employees where date(hire_time) ='2018‐09‐30';

ここに画像の説明を挿入します

DATE(日付)関数は、日付の年月日のみを抽出します。

SELECT DATE('2018-09-30 :00:00:00') AS 日期

ここに画像の説明を挿入します
インデックスを使用せずにクエリを年、月、日に変更するのはなぜですか?
理由:インデックスツリーに格納されているデータは、年、月、日、時、分、秒で格納されているため、年、月、日だけを一致させることはできず、完全に一致させる方法はありません。インデックスにアクセスできない場合。
最適化のアイデア:

EXPLAIN SELECT * FROM employees WHERE hire_time >='2018-09-30 00:00:00' 
AND hire_time <='2018-09-30 23:59:59'

ここに画像の説明を挿入します

デモンストレーション後、インデックスを削除します

ALTER TABLE `employees` DROP INDEX `idx_hire_time`

4. ストレージ エンジンは、インデックス内の範囲条件の右側の列を使用できません。

=》SQLの例=》

EXPLAIN SELECT * FROM employees WHERE NAME= 'LiLei' AND age = 22 AND POSITION ='manager';

ここに画像の説明を挿入します
ken_len から、3 つの条件すべてにインデックスが付けられていることがわかります。
=》SQLの例=》

EXPLAIN SELECT * FROM employees WHERE NAME= 'LiLei' AND age > 22 AND POSITION ='manager';

ここに画像の説明を挿入します

key_len から、インデックス内の名前フィールドと年齢フィールドのみが使用され、位置フィールドにはインデックスが付けられていないことがわかります。
なぜそうなるのでしょうか?
まず、名前が決まると、名前に対して年齢順、役職順になります。
ただし、第二の条件は、年齢がそれ以上の場合、つまり年齢が不確かな場合であり、必ずしも立場が整っているとは限らない。ただし、年齢は特定の名前に対して相対的な順序でなければなりません。インデックス年齢はインデックス化できますが、ポジションはインデックス化できません。
つまり、name='LiLei' AND age > 22 の範囲では、ポジションは連続して表示されません。つまり、順序付けされていません。おそらく、23 歳未満ではあるが 24 歳未満ではないコンテンツがあり、30 歳未満では利用可能である可能性があります。この場合、この範囲のデータをスキャンして、POSITION ='manager' に一致するデータを確認する必要があります。

5. カバーインデックス (インデックスのみにアクセスするクエリ (インデックス列にはクエリ列が含まれる)) を使用し、select * ステートメントを減らすようにしてください。

カバリング インデックスについては上で説明しましたが、select * の使用が推奨されないのはなぜですか? カバリング インデックスを使用する場合、インデックス内のリーフ ノードがすべての列データを保存するわけではなく、特定の列のインデックスである可能性があるためです。したがって、できる限り第 2 レベルに近いクエリを実行し、関連するデータにインデックスを付けます。
=》SQLの例=》

EXPLAIN SELECT NAME,age FROM employees WHERE NAME= 'LiLei' AND age = 23 AND POSITION ='manager';

ここに画像の説明を挿入します

EXPLAIN SELECT * FROM employees WHERE NAME= 'LiLei' AND age = 23 AND POSITION ='manager';

ここに画像の説明を挿入します

6. Mysql は、等しくない (!= または <>)、not in、notowned を使用するとインデックスを使用できません。その場合、テーブル全体のスキャンが行われます。

< より小さい、> より大きい、<=、>= これらの場合、mysql 内部オプティマイザーは、取得率、テーブル サイズなどの複数の要素に基づいてインデックスを使用するかどうかを評価します。

EXPLAIN SELECT * FROM employees WHERE NAME != 'LiHua';

ここに画像の説明を挿入します
キー列を見るとインデックスがないことがわかりますが、なぜこのままなのでしょうか?
MySQL にはインデックスを使用するかどうかの計算ロジックが設定されているため、インデックスよりもテーブル全体を直接スキャンした方が速いのではないかと思われるかもしれませんが、一般にテーブルのデータが小さい場合は状況が発生する可能性があります。

7.is null、is not null は通常インデックスを使用できません。

EXPLAIN SELECT * FROM employees WHERE NAME IS NULL

ここに画像の説明を挿入します

8.ワイルドカード ('$abc...') で開始する場合と同様、mysql インデックスの失敗はテーブル全体のスキャン操作になります

EXPLAIN SELECT * FROM employees WHERE NAME LIKE '%Li'

ここに画像の説明を挿入します

EXPLAIN SELECT * FROM employees WHERE NAME LIKE 'Li%'

ここに画像の説明を挿入します

B+ ツリーの構造と組み合わせると、最初のパーセント記号、つまりサフィックスに Li が含まれる文字列が、この範囲の B+ ツリー内で必ずしも順序付けされているわけではなく、どうしようもないことがわかります。インデックスに。
ただし、パーセント記号と先頭の Li が固定されると、これらも Li で始まるデータになります。B+ ツリーに保存されたデータは、インデックス付けできるように順序付けされている必要があります。

では、最初のパーセント記号を使用する必要がある場合、それを最適化するにはどうすればよいでしょうか? 次に、カバーリングインデックスを使用させてみます。

EXPLAIN SELECT NAME,age,POSITION FROM employees WHERE NAME LIKE '%Li'

ここに画像の説明を挿入します
タイプを ALL からインデックスに変更します。

like の後のパーセント記号は、定数クエリと同等です。

たとえば、KK% は = constant と同等、%KK と %KK% は range と同等です。

9. 一重引用符のない文字列のインデックスは無効です。

EXPLAIN SELECT * FROM employees WHERE NAME = '1000';

ここに画像の説明を挿入します

EXPLAIN SELECT * FROM employees WHERE NAME = 1000;

ここに画像の説明を挿入します

10. または は控えめに使用する

クエリに使用する場合、mysql は必ずしもインデックスを使用する必要はありません。mysql の内部オプティマイザーは、取得率やテーブル サイズなどの複数の要素に基づいてインデックスを使用するかどうかを評価します。詳細については、フォローアップのブログ範囲クエリを参照してください。最適化コンテンツ。

EXPLAIN SELECT * FROM employees WHERE NAME = 'LiHua' OR NAME = 'HanLei';

ここに画像の説明を挿入します

11. 範囲クエリの最適化

インデックスを追加する

ALTER TABLE `employees` ADD INDEX `idx_age` (`age`) USING BTREE ;
EXPLAIN SELECT * FROM employees WHERE age >=1 AND age <=2000;

ここに画像の説明を挿入します

インデックスを使用しない理由: MySQL の内部オプティマイザーは、検索率やテーブル サイズなどの複数の要素に基づいて、全体としてインデックスを使用するかどうかを評価します。
たとえば、この例では、単一のデータ クエリが大きすぎるため、オプティマイザが最終的にインデックスを使用しないことを選択する可能性があります。最適化
方法: 大きな範囲を複数の小さな範囲に分割できます。

EXPLAIN SELECT * FROM employees WHERE age >=1 AND age <=1000;

ここに画像の説明を挿入します

EXPLAIN SELECT * FROM employees WHERE age >=1001 AND age <=2000;

ここに画像の説明を挿入します
なぜ最初のものは行かなかったが、二番目のものはできたのでしょうか?
Mysql には実際には独自のクエリ ロジック セットがあり、各プランにスコアを与え、最終的に最もスコアの高いプランに従って実行されます。この場合、フル テーブル スキャンまたはインデックス クエリが選択されます。評価に関する内容については後のブログで説明しますが、ここではこのロジックを紹介します。

最後にインデックスを削除します。

ALTER TABLE `employees` DROP INDEX `idx_age`;

おすすめ

転載: blog.csdn.net/xiaobai_july/article/details/132653013