MySQLインデックスの最適化(1)
シミュレーションデータ
CREATE TABLE staffs(
id INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(24) DEFAULT NULL COMMENT'姓名',
`age` INT NOT NULL DEFAULT 0 COMMENT'年龄',
`pos` VARCHAR(20) NOT NULL DEFAULT'' COMMENT'职位',
`add_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT'入职时间'
)CHARSET utf8 COMMENT'员工记录表';
INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('z3',22,'manager',NOW());
INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('July',23,'dev',NOW());
INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES('2000',23,'dev',NOW());
INSERT INTO staffs(`name`,`age`,`pos`,`add_time`) VALUES(NULL,23,'test',NOW());
ALTER TABLE staffs ADD INDEX index_staffs_nameAgePos(`name`,`age`,`pos`)
インデックスの順序を表示
インデックス最適化の場合
- 最良の左プレフィックスルール
- フルマッチ
- インデックス列では計算されません
- スコープ後のすべての障害
- カバーインデックスより多くの使用
- 不平等な使用は無効になります
- NULL値を使用する場合は注意してください
- ファジークエリプラス右
- 一重引用符付きの文字列
- 使用または照会しないようにしてください
最良の左プレフィックスルール
インデックスの左端の列からクエリを開始し、インデックスの列をスキップしないでください
インデックスを使用するためのフィルター条件は、インデックスが作成された順序で満たされる必要があります。フィールドがスキップされると、インデックスの後のフィールドは使用できなくなります。
複数列のインデックスは、最初に最初の列に従って並べ替えられ、次に最初の列の並べ替え順序に基づいて2番目の列が並べ替えられます。最初の列がない場合は、2番目の列に直接アクセスし、2番目の列はなしである必要があります。次の列へのアクセスはインデックスを使用しません
インデックスで検索
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July'
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' and age = 15
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' and age = 15 and pos= 'dev'
インデックス順以外のクエリ
EXPLAIN SELECT * FROM staffs WHERE age = 15 and pos= 'dev' --跳过第一个索引
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' and pos= 'dev' -- 跳过第二个索引
フルマッチ
クエリフィールドは順序に従ってインデックスで一致させることができ、速度は現時点で最速です。
インデックス列では計算されません
インデックス列に対して操作(計算、関数、(自動または手動)型変換)を実行しないでください。インデックスが失敗し、全表スキャンになる可能性があります。
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July'
EXPLAIN SELECT * FROM staffs WHERE LEFT(NAME,4) = 'July' --使用函数
EXPLAIN SELECT * FROM staffs WHERE NAME = '2000'
EXPLAIN SELECT * FROM staffs WHERE NAME = 2000 --使用类型转换
スコープ後のすべての障害
範囲クエリを使用した後、範囲内のレコードが多すぎると、カスタムインデックスから主キーインデックスへのマッピングに時間がかかりすぎ、テーブル全体ほど高速ではないため、インデックスは無効になります。スキャン
提案:最後に範囲が指定されている可能性のあるフィールドのインデックス順を入力してください
カバーインデックスより多くの使用
カバーインデックスを使用すると(インデックスを使用)、検索効率が向上します。インデックス列にのみアクセスするクエリ(インデックス列はクエリ列と同じです。select*は使用しないでください)
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' and age = 23 and pos= 'dev'
EXPLAIN SELECT NAME,age,pos FROM staffs WHERE NAME = 'July' and age = 23 and pos= 'dev'
不平等な使用は無効になります
等しくない(!=または<>)を使用すると、インデックスが無効になる場合があります
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July'
EXPLAIN SELECT * FROM staffs WHERE NAME != 'July'
EXPLAIN SELECT * FROM staffs WHERE NAME <> 'July'
低バージョンのMySQLデータベースでは、次の場合にインデックスが失敗し、MySQL 8を使用しています。データベースの内部最適化が実行されます。インデックスは無効ではありませんが、タイプがrefからrangeに変わり、パフォーマンスは明らかに低下します。
NULL値を使用する場合は注意してください
場合は許可フィールドが空である場合、
- IS NULLは、インデックス障害を引き起こしません
- IS NOT NULLを使用すると、インデックスが無効になります
ファジークエリプラス右
ワイルドカード%で始まるクエリのように、インデックスは失敗し、全表スキャンになります
EXPLAIN SELECT * FROM staffs WHERE NAME like '%July%' --左右都有通配符
EXPLAIN SELECT * FROM staffs WHERE NAME like '%July' --模糊查询加左边
EXPLAIN SELECT * FROM staffs WHERE NAME like 'July%' --模糊查询加右边
問題:「%string%」のようなインデックスを解決する方法が有効にならない:カバーインデックスを使用する(クエリフィールドはインデックスフィールドと可能な限り一致する必要があります)
一重引用符付きの文字列
EXPLAIN SELECT * FROM staffs WHERE NAME = '2000'
EXPLAIN SELECT * FROM staffs WHERE NAME = 2000 --使用类型转换
使用または照会しないようにしてください
SELECT * FROM staffs WHERE NAME = 'July' OR NAME = 'z3'
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' OR NAME = 'z3'
タイプは使用後に範囲になりますまたは
簡単な面接の質問
create table test03(
id int primary key not null auto_increment,
c1 char(10),
c2 char(10),
c3 char(10),
c4 char(10),
c5 char(10));
insert into test03(c1,c2,c3,c4,c5) values ('a1','a2','a3','a4','a5');
insert into test03(c1,c2,c3,c4,c5) values ('b1','b2','b3','b4','b5');
insert into test03(c1,c2,c3,c4,c5) values ('c1','c2','c3','c4','c5');
insert into test03(c1,c2,c3,c4,c5) values ('d1','d2','d3','d4','d5');
insert into test03(c1,c2,c3,c4,c5) values ('e1','e2','e3','e4','e5');
create index idx_test03_c1234 on test03(c1,c2,c3,c4);
実行計画分析
ケースA
EXPLAIN SELECT * FROM test03 WHERE c1 = 'a1'
EXPLAIN SELECT * FROM test03 WHERE c1 = 'a1' and c2 = 'a2'
EXPLAIN SELECT * FROM test03 WHERE c1 = 'a1' and c2 = 'a2' and c3 = 'a3'
EXPLAIN SELECT * FROM test03 WHERE c1 = 'a1' and c2 = 'a2' and c3 = 'a3' and c4 = 'a4'
ケースB
EXPLAIN SELECT * FROM test03 WHERE c4 = 'a4' and c3 = 'a3' and c2 = 'a2' and c1 = 'a1' --MySQL内部优化,依然使用索引
EXPLAIN SELECT * FROM test03 WHERE c1 = 'a1' and c2 = 'a2' and c4 = 'a4' and c3 > 'a3'
EXPLAIN SELECT * FROM test03 WHERE c1 = 'a1' and c2 = 'a2' and c4 = 'a4' ORDER BY c3 --c3用于排序
EXPLAIN SELECT * FROM test03 WHERE c1 = 'a1' and c2 = 'a2' ORDER BY c3
EXPLAIN SELECT * FROM test03 WHERE c1 = 'a1' and c2 = 'a2' ORDER BY c4 --出现文件排序
ケースC
EXPLAIN SELECT * FROM test03 WHERE c1 = 'a1' and c5 = 'a5' ORDER BY c2,c3
EXPLAIN SELECT * FROM test03 WHERE c1 = 'a1' and c5 = 'a5' ORDER BY c3,c2
EXPLAIN SELECT * FROM test03 WHERE c1 = 'a1' and c2 = 'a2' ORDER BY c2,c3
EXPLAIN SELECT * FROM test03 WHERE c1 = 'a1' and c2 = 'a2' ORDER BY c3,c2
EXPLAIN SELECT * FROM test03 WHERE c1 = 'a1' ORDER BY c3,c2
ケースD
私のデータベースは次のケースを実行できないため、SQLステートメントしかありません。スクリーンショットを確認していません。
EXPLAIN SELECT * FROM test03 WHERE c1 = 'a1' AND c4 = 'a4' GROUP BY c2,c3
EXPLAIN SELECT * FROM test03 WHERE c1 = 'a1' AND c4 = 'a4' GROUP BY c3,c2
- 命令は範囲です
- Group byはグループ化であるため、最初に並べ替える必要があるため、一時テーブルが作成されます
最適化式
完全な値は私のお気に入りと一致し、左端のプレフィックスを守る必要があります。
先頭の兄弟は死ぬことはできず、中間の兄弟は壊すことはできません。
インデックス列の計算が少なく、範囲が完全に無効です。
パーセントが右側に書き込まれるように、インデックスをカバーしても星は書き込まれません。
空を待機しません。値にもまたはがあります。インデックスの失敗にはlessを使用します
。VAR引用符が失われることはなく、高度なSQLは難しくありません。