目次
1. テーブル情報
2. Explain を使用する 2 つの方法
3. Explain の列
-
id列
-
select_type列
-
table列
-
type列
-
possible_keys列
-
キー列
-
key_len列
-
参照列
-
rows列
-
追加の列
4. インデックス作成のベスト プラクティス
EXPLAIN キーワードを使用して SQL ステートメントを実行するオプティマイザーをシミュレートし、MySQL が SQL ステートメントをどのように処理するかを確認します。クエリ ステートメントまたは構造のパフォーマンスのボトルネックを分析する
Explain の使用例を次に示します。
select ステートメントの前に Explain キーワードを追加すると、MySQL はクエリにマークを設定します。クエリが実行されると、この SQL を実行する代わりに実行プラン情報が返されます (from にサブクエリが含まれている場合、サブクエリは引き続き実行されます)。結果を一時テーブルに入れます)
1. テーブル情報
Mysql バージョン: 5.6.33
テーブルが存在する場合はテーブルを削除しますactor
。
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;
INSERT INTO actor
( id
, name
, update_time
) 値 (1,'a','2017-12-22 15:27:18'), (2,'b','2017-12-22 15:27:18'), ( 3、'c'、'2017-12-22 15:27:18');
テーブルが存在する場合はテーブルを削除しますfilm
。
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;
INSERT INTO film
( id
, name
) VALUES (3,'film0'),(1,'film1'),(2,'film2');
テーブルが存在する場合はテーブルを削除しますfilm_actor
。
CREATE TABLE film_actor
(
id
int(11) NOT NULL,
film_id
int(11) NOT NULL,
actor_id
int(11) NOT NULL,
remark
varchar(255) DEFAULT NULL,
PRIMARY KEY ( id
),
KEY idx_film_actor_id
( film_id
, actor_id
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO film_actor
( id
, film_id
, actor_id
) VALUES (1,1,1),(2,1,2),(3,2,1);
2. Explain を使用する 2 つの方法
1) Explain 拡張: Explain に基づきます。クエリの最適化に関する情報。その直後に、show warnings コマンドを使用して最適化されたクエリ ステートメントを取得し、オプティマイザが何を最適化したかを確認できます。また、半分の比率値であるフィルターされた列もあります。 rows * filtered/100 は、Explain 内の前のテーブルに接続される行数を推定できます (前のテーブルは、Explain 内の ID 値が現在のテーブル表面の ID 値より小さい)
mysql> 拡張選択 * from film where id = 1; を説明します。
mysql> 警告を表示;
2) Explain パーティション: Explain と比較して、パーティション フィールドが 1 つ多く、クエリがパーティション テーブルに基づいている場合、クエリがアクセスするパーティションが表示されます。
3. Explain の列
- id
欄の数字はセレクトの通し番号で、セレクトの数だけidが存在し、セレクトが出現した順にidの順番が増えます。MySQL は、選択クエリを単純なクエリ (SIMPLE) と複雑なクエリ (PRIMARY) に分割します。
複雑なクエリは、単純なサブクエリ、派生テーブル (from ステートメント内のサブクエリ)、およびユニオン クエリの 3 つのカテゴリに分類されます。
id列が大きいほど実行優先度が高く、idが同じ場合は上から順に実行され、idがNULLの場合は最後に実行されます。
(1) 単純なサブクエリ
mysql> Explain select (俳優制限 1 から 1 を選択) from film;
(2) from句のサブクエリ
mysql> Explain select ID from (フィルムから ID を選択) を der;
このクエリが実行されると、der というエイリアスが付けられた一時テーブルが存在し、外部選択クエリはこの一時テーブルを参照します。
(3) ユニオンクエリ
mysql> Explain select 1 Union all select 1;
ユニオンの結果は常に匿名の一時テーブルに配置されます。一時テーブルは SQL に現れないため、その ID は NULL です。
- select_type 列
select_type は、対応する行が単純なクエリであるか複雑なクエリであるかを示し、複雑なクエリである場合は、上記 3 つの複雑なクエリのどれであるかを示します。
(1) シンプル: 単純なクエリ。クエリにはサブクエリとユニオンが含まれていません
mysql> Explain select * from film where id = 2;
(2) プライマリ: 複雑なクエリの最も外側の選択
(3) サブクエリ: select に含まれるサブクエリ (from 句には含まれない)
(4) 派生: from 句に含まれるサブクエリ。MySQL は結果を一時テーブルに保存します。このテーブルは派生テーブル (派生テーブルの英語の意味) とも呼ばれます。
この例を使用して、プライマリ、サブクエリ、および派生型を理解します。
mysql> Explain select (ID = 1 の俳優から 1 を選択) from (ID = 1 の映画から * を選択) der;
(5) Union: Union 内の 2 番目以降の選択
(6) ユニオン結果: ユニオン一時テーブルから結果を取得する場合に選択します。
この例を使用して、共用体と共用体の結果の型を理解してください。
mysql> Explain select 1 Union all select 1;
- テーブル列
この列は、Explain の行がどのテーブルにアクセスしているかを示します。
from 句にサブクエリがある場合、テーブルの列は現在のクエリが id=N のクエリに依存していることを示す形式になるため、id=N のクエリが最初に実行されます。
ユニオンがある場合、UNION RESULT のテーブル列の値は <union1,2> で、1 と 2 はユニオンに参加している選択行 ID を表します。
- type 列
この列は関連付けタイプまたはアクセス タイプを表します。つまり、MySQL はテーブル内の行の検索方法とデータ行レコードのおおよその範囲を決定します。
最適なものから最悪なものまで、system > const > eq_ref > ref > range >index > ALL となります。
一般に、クエリが範囲レベル (できれば ref) に達していることを確認する必要があります。
(1) NULL: MySQL は最適化フェーズ中にクエリ ステートメントを分解でき、実行フェーズ中にテーブルやインデックスにアクセスする必要はありません。たとえば、インデックス列の最小値の選択は、実行中にテーブルにアクセスせずにインデックスのみを検索することで実行できます。
mysql> フィルムから min(id) を選択する説明します。
(2) const、system: mysql はクエリの特定の部分を最適化し、それを定数に変換できます (show warnings の結果を確認できます)。主キーまたは一意キーのすべての列が定数と比較される場合、テーブルには一致する行が 1 つだけあり、読み取られるのは 1 回なので高速です。System は const の特殊なケースで、テーブル内で一致するタプルが 1 つだけの場合、それは system です。
mysql> 拡張された select * from (select * from film where id = 1) tmp; を説明します。
mysql> 警告を表示;
(3) eq_ref: 主キーまたは一意キー インデックスのすべての部分が接続で使用され、最大でも 1 つの条件を満たすレコードのみが返されます。これは const 以外では最適な結合タイプである可能性があり、単純な選択クエリはこのタイプでは表示されません。
mysql> Explain select * from film_actor left join film on film_actor.film_id = film.id;
(4) ref: eq_ref と比較して、一意のインデックスを使用せず、通常のインデックスまたは一意のインデックスの一部のプレフィックスを使用しますが、インデックスは特定の値と比較する必要があり、修飾された行が複数見つかる可能性があります。
- 単純な選択クエリ、名前は共通インデックス (一意ではないインデックス) mysql> Explain select * from film where name = "film1";
2. 関連テーブル クエリの idx_film_actor_id は、film_id とactor_id の結合インデックスであり、film_actor の左側のプレフィックス film_id 部分がここで使用されます。
mysql> 説明 フィルムの左からフィルム ID を選択して、フィルム.id = film_actor.film_id にフィルム_アクターを結合します。
(5) range: 範囲スキャンは通常、in()、between、>、<、>= およびその他の操作で発生します。インデックスを使用して、指定された範囲の行を取得します。
mysql> Explain select * from Actor where id > 1;
(6) インデックス: テーブル インデックス全体をスキャンします。通常、ALL よりも高速です。(インデックスはインデックスから読み取られ、すべてはハードディスクから読み取られます)
mysql> 説明 select * from film;
(6) ALL: フル テーブル スキャン。これは、mysql が最初から最後まで必要な行を見つける必要があることを意味します。通常、これには最適化のためにインデックスを追加する必要があります
mysql> Explain select * from Actor;
- possible_keys 列
この列は、クエリが検索に使用できるインデックスを示します。
Explain では、 possible_keys に列があるにもかかわらず、 key に NULL が表示される状況が発生することがあります。これは、テーブルにデータがあまりなく、MySQL はインデックスがこのクエリにはあまり役に立たないと考えているため、完全なインデックスを選択します。テーブルクエリ。
列が NULL の場合、関連付けられたインデックスはありません。この場合、where 句をチェックして適切なインデックスを作成してクエリのパフォーマンスを向上できるかどうかを確認し、explain を使用してその効果を確認できます。
- キー列
この列は、mysql がテーブルへのアクセスを最適化するために実際に使用するインデックスを示します。
インデックスが使用されていない場合、列は NULL になります。MySQL に possible_keys 列のインデックスの使用または無視を強制する場合は、クエリで強制インデックスまたは無視インデックスを使用します。
- key_len 列
この列は、インデックス内で MySQL によって使用されるバイト数を示します。この値は、インデックス内のどの列が使用されるかを計算するために使用できます。
たとえば、film_actor のジョイント インデックス idx_film_actor_id は、film_id と Actor_id という 2 つの int 列で構成され、各 int は 4 バイトです。結果の key_len=4 から、クエリが最初の列である film_id 列を使用してインデックス検索を実行していることが推測できます。
mysql> Explain select * from film_actor where film_id = 2;
key_len の計算ルールは次のとおりです。
弦
char(n): n バイト長
varchar(n): 2 バイトの格納文字列長、utf-8 の場合、長さは 3n + 2 の
数値型
tinyint: 1 バイト
smallint: 2 バイト
int: 4 バイト
bigint: 8 バイトの
時間型
date: 3 バイト
timestamp: 4 バイト
datetime: 8 バイト
フィールドが NULL を許可する場合、NULL かどうかを記録するために 1 バイトが必要です
知らせ:
1. 索引最大长度是768字节,当字符串过长时,mysql会做一个类似左前缀索引的处理,将前半部分的字符提取出来做索引
2. 一般情况下key_len值越小越好, 索引越短,一是节省空间,而且比较速度较快
-
ref 列
この列には、キー列レコードのインデックス内のテーブル ルックアップ値で使用される列または定数が表示されます。一般的なものには、const (定数)、フィールド名 (例: film.id) が含まれます。 -
rows 列
この列は、MySQL が読み取りおよび検出すると推定する行数です。これは結果セット内の行数ではないことに注意してください。 -
追加列
この列には追加情報が表示されます。一般的に重要な値は次のとおりです。
(1) インデックスの使用: クエリ対象の列はインデックスの対象となり、where フィルター条件はインデックスの先頭列 (一番左のインデックス) となり、高いパフォーマンスが得られます。一般に、カバー インデックスが使用されます (インデックスには、クエリされたすべてのフィールドが含まれます)。innodb の場合、補助インデックスであればパフォーマンスが大幅に向上します。
mysql> Explain select film_id from film_actor where film_id = 1;
(2) where の使用: クエリされた列はインデックスの対象外であり、where フィルター条件はインデックスの先頭列ではありません。
mysql> Explain select * from Actor where name = 'a';
(3) where の使用 インデックスの使用: クエリ対象の列はインデックスの対象であり、where フィルター条件はインデックス列の 1 つですが、インデックスの先頭の列ではありません。つまり、修飾されたデータをインデックス検索を通じて直接クエリすることはできません。 . インデックスの使用は選択を表します カバリングインデックスが使用されます
mysql> Explain select film_id from film_actor where actress_id = 1;
(4) NULL: クエリされた列はインデックスの対象外であり、where フィルター条件がインデックスの先頭列である、つまりインデックスは使用されていますが、一部のフィールドはインデックスの対象になっていないことを意味します。これは達成する必要があります。 「テーブルに戻る」ということで、純粋に使われていないインデックスですが、インデックスが全く使われていないわけではありません。
mysql>explain select * from film_actor where film_id = 1;
(5) インデックス条件の使用: where の使用と同様、クエリされた列はインデックスによって完全にカバーされておらず、where 条件は先頭の列の範囲です。
mysql> Explain select * from film_actor where film_id > 1;
(6) 一時テーブルの使用: mysql はクエリを処理するために一時テーブルを作成する必要があります。このような場合は、通常、インデックスを使用して最適化を行う必要があります。
-
Actor.name にはインデックスがありません。この時点で、mysql> Explain select Unique Name を Actor から区別するために一時テーブルが作成されます。
-
film.name は idx_name インデックスを確立しました。この時点では、エクストラはクエリ時にインデックスを使用しており、一時テーブルは使用されません。mysql> Explain select unique name from film;
(7) filesort の使用: mysql は、インデックス順にテーブルから行を読み取る代わりに、外部インデックスを使用して結果を並べ替えます。このとき、mysql は接続タイプに従って対象となるすべてのレコードを参照し、ソート キーと行ポインタを保存してから、キーをソートして行情報を順番に取得します。この場合、通常はインデックスを使用した最適化を検討する必要があります。
-
Actor.name はインデックスを作成していません。アクター テーブル全体を参照し、ソート キー名と対応する ID を保存し、名前をソートして行レコードを取得します。
-
film.name は idx_name インデックスを確立しました。この時点で、追加のクエリは、indexmysql> Explain select * from film order by name; を使用しています。
4. インデックスのベストプラクティス
テーブル情報
CREATE TABLE employees
(
id
int(11) NOT NULL AUTO_INCREMENT,
name
varchar(24) NOT NULL DEFAULT '' COMMENT 'name',
age
int(11) NOT NULL DEFAULT '0' COMMENT 'age',
position
varchar( 20 ) NOT NULL DEFAULT '' COMMENT 'position'、
hire_time
タイムスタンプ NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'entry time'、
PRIMARY KEY ( id
)、
KEY idx_name_age_position
( name
, age
, position
) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='従業員レコード形状';
INSERT INTO 従業員(名前,年齢,役職,入社時刻) VALUES('LiLei',22,'manager',NOW());
INSERT INTO 従業員(名前,年齢,役職,入社時刻) VALUES('HanMeimei', 23,'dev',NOW());
INSERT INTO 従業員(名前,年齢,役職,雇用時間) VALUES('Lucy',23,'dev',NOW());
最佳实実践
- すべての値を一致させる
EXPLAIN SELECT * FROM 従業員 WHERE name= 'LiLei';
EXPLAIN SELECT * FROM 従業員 WHERE 名前 = 'LiLei' AND 年齢 = 22;
EXPLAIN SELECT * FROM 従業員 WHERE 名前 = 'LiLei' AND 年齢 = 22 AND 役職 ='マネージャー';
- ベストレフトプレフィックスルール
複数の列にインデックスが付けられている場合は、一番左のプレフィックス ルールに従う必要があります。これは、クエリがインデックスの左端の列から開始され、インデックス内の列をスキップしないことを意味します。
EXPLAIN SELECT * FROM 従業員 WHERE 年齢 = 22 AND 役職 ='マネージャー';
EXPLAIN SELECT * FROM 従業員 WHERE ポジション = 'マネージャー';
EXPLAIN SELECT * FROM 従業員 WHERE name = 'LiLei';
- インデックス列に対して操作 (計算、関数、(自動または手動) 型変換) を実行しないと、インデックスが失敗し、テーブル全体のスキャンが行われます。
EXPLAIN SELECT * FROM 従業員 WHERE name = 'LiLei';
EXPLAIN SELECT * FROM 従業員 WHERE left(name,3) = 'LiLei';
- ストレージ エンジンはインデックス内の範囲条件の右側にある列を使用できません
EXPLAIN SELECT * FROM 従業員 WHERE 名前 = 'LiLei' AND 年齢 = 22 AND 役職 ='マネージャー';
EXPLAIN SELECT * FROM 従業員 WHERE 名前 = 'LiLei' AND 年齢 > 22 AND 役職 ='マネージャー';
- カバーするインデックス (インデックスのみにアクセスするクエリ (インデックス列にはクエリ列が含まれる)) を使用し、select * ステートメントを減らすようにしてください。
EXPLAIN SELECT 名前、年齢 FROM 従業員 WHERE 名前 = 'LiLei' AND 年齢 = 23 AND 役職 ='マネージャー';
EXPLAIN SELECT * FROM 従業員 WHERE 名前 = 'LiLei' AND 年齢 = 23 AND 役職 ='マネージャー';
- MySQL は、等しくない (!= または <>) を使用するとインデックスを使用できず、テーブル全体のスキャンが発生します。
EXPLAIN SELECT * FROM 従業員 WHERE name != 'LiLei'
- null である、null ではない、インデックスは使用できない
EXPLAIN SELECT * FROM 従業員 WHERE 名前が null
- like がワイルドカード ('$abc...') で始まる場合、mysql インデックスの失敗はテーブル全体のスキャン操作になります
EXPLAIN SELECT * FROM 従業員 WHERE 名前 ('%Lei' など)
EXPLAIN SELECT * FROM 従業員 WHERE 名前 (「Lei%」など)
質問: '% string%' インデックスが使用されていないなどの問題を解決するにはどうすればよいですか?
a) カバリングインデックスを使用する場合、クエリフィールドはカバリングインデックスフィールドである必要があります。
EXPLAIN SELECT 名前、年齢、役職 FROM 従業員 WHERE name like '%Lei%';
b) カバーインデックスが指すフィールドが varchar(380) または 380 を超えるフィールドの場合、カバーインデックスは失敗します。
- 一重引用符のない文字列インデックスは無効です
EXPLAIN SELECT * FROM 従業員 WHERE name = '1000';
EXPLAIN SELECT * FROM 従業員 WHERE name = 1000;
- 「less or or in」を使用します。クエリに使用する場合、主キー以外のフィールドは無効になります。主キーインデックスは有効になる場合と無効な場合があります。データ量によって異なります。詳細もクエリによって異なります。 MySQLの最適化結果。
EXPLAIN SELECT * FROM 従業員 WHERE name = 'LiLei' または name = 'HanMeimei';
5. 概要:
KK% は = 定数と等価、%KK と %KK% は range と等価です
—————————————
著作権表示: この記事は CSDN ブロガー「AnEra」によるオリジナル記事であり、以下のとおりです。 CC 4.0 BY-SA 著作権規約。転載する場合は、元のソースリンクとこの声明を添付してください。
元のリンク: https://blog.csdn.net/qq_38975553/article/details/104134587