ツールの紹介を説明します
使用すると、SQL文を実行するクエリまたはSELECT文の前に、MySQLがクエリにマークを設定します説明キーワード構造の増加でパフォーマンスのボトルネックを分析し、クエリを返す実行プラン情報を実行するために、オプティマイザをシミュレートすることができ、キーワードEXPLAIN代わりに、SQL注意が実行されますから、サブクエリが含まれている場合は、一時テーブルにサブクエリ結果を実行します
分析例1を説明します
例表:
表1
DROP TABLE IF EXISTS `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`) VALUES
(1,'a','2017‐12‐22 15:27:18'),(2,'b','2017‐12‐22 15:27:18'),(3,'c','2017‐12‐22 15:27:18');
表2
DROP TABLE IF EXISTS `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');
表3
DROP TABLE IF EXISTS `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行になります
二つの変形を説明
1)拡張について説明し、余分なクエリの最適化は、説明に基づいていくつかの情報を提供します。どのようなオプティマイザの最適化を参照するには、show警告コマンドによってクエリが続いて最適化することができます
主キーのクエリ:
explain select * from pea_goods where pea_goods_Id=190919181614003;
出力
ID | SELECT_TYPE | テーブル | パーティション | タイプ | possible_keys | キー | key_lenに | REF | 行 | フィルタ | エクストラ |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | pea_goods | CONST | PRIMARY | PRIMARY | 8 | CONST | 1 | 100.00 |
警告を表示します。
出力
レベル | コード | メッセージ |
---|---|---|
注意 | 1003 | / * *#1 / SELECT '190,919,181,614,003' AS SELECT pea_goods_id 、 '190,919,145,343,001' AS pea_supplier_id 、 '190,919,175,230,001' AS pea_customer_id 、 'エスタゾラム' AS goods_name 、 'XXX007' AS goods_code 、'10 'AS goods_type 、' T 'AS goods_unit 、AS NULL safe_date 、100' 'AS width ' 100 'AS length ' 100 'AS height ' 100 'AS weight 、AS NULL volume 、' 10000.08 'AS price 、' 2019年10月30日午後十一時59分59秒'AS warranty '30' AS allowed_day '慎重に、危険な'AS吹き付けdescription 、AS NULL line_no 、' Y 'AS is_active ' 190,815,135,958,005 'AS created_by 、' 2019年9月19日午後06時16分14秒'AS created ' 190,815,135,958,005 'AS updated_by 、' 2019年10月10日夜4時53分02秒「AS updated 「190,815,135,742,002」AS ad_client_id からvip_tulin 。pea_goods ここで、1 |
非プライマリキーのクエリ
explain select * from pea_goods where pea_customer_id=190919175230001;
出力
ID | SELECT_TYPE | テーブル | パーティション | タイプ | possible_keys | キー | key_lenに | REF | 行 | フィルタ | エクストラ |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | pea_goods | すべて | 50 | 10.00 | 使い方 | どこ |
警告を表示します。
出力
レベル | コード | メッセージ |
---|---|---|
注意 | 1003 | / * |
select#1 */ select `vip_tulin`.`pea_goods`.`pea_goods_id` AS `pea_goods_id`,`vip_tulin`.`pea_goods`.`pea_supplier_id` AS `pea_supplier_id`,`vip_tulin`.`pea_goods`.`pea_customer_id` AS `pea_customer_id`,`vip_tulin`.`pea_goods`.`goods_name` AS `goods_name`,`vip_tulin`.`pea_goods`.`goods_code` AS `goods_code`,`vip_tulin`.`pea_goods`.`goods_type` AS `goods_type`,`vip_tulin`.`pea_goods`.`goods_unit` AS `goods_unit`,`vip_tulin`.`pea_goods`.`safe_date` AS `safe_date`,`vip_tulin`.`pea_goods`.`width` AS `width`,`vip_tulin`.`pea_goods`.`length` AS `length`,`vip_tulin`.`pea_goods`.`height` AS `height`,`vip_tulin`.`pea_goods`.`weight` AS `weight`,`vip_tulin`.`pea_goods`.`volume` AS `volume`,`vip_tulin`.`pea_goods`.`price` AS `price`,`vip_tulin`.`pea_goods`.`warranty` AS `warranty`,`vip_tulin`.`pea_goods`.`allowed_day` AS `allowed_day`,`vip_tulin`.`pea_goods`.`description` AS `description`,`vip_tulin`.`pea_goods`.`line_no` AS `line_no`,`vip_tulin`.`pea_goods`.`is_active` AS `is_active`,`vip_tulin`.`pea_goods`.`created_by` AS `created_by`,`vip_tulin`.`pea_goods`.`created` AS `created`,`vip_tulin`.`pea_goods`.`updated_by` AS `updated_by`,`vip_tulin`.`pea_goods`.`updated` AS `updated`,`vip_tulin`.`pea_goods`.`ad_client_id` AS `ad_client_id` from `vip_tulin`.`pea_goods` where (`vip_tulin`.`pea_goods`.`pea_customer_id` = 190919175230001)
idカラム
ID | SELECT_TYPE | テーブル | パーティション | タイプ | possible_keys | キー | key_lenに | REF | 行 | フィルタ | エクストラ |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | pea_goods | すべて | 50 | 10.00 | 使い方 | どこ |
id 列的编号是select 的序列号,有几个select 就有几个id,并且顺序是按照select 顺序增长的。
id 越大执行优先级越高,id 相同从上往下执行,id 为null 最后执行。
2. SELECT_TYPE列
シンプル:サブクエリ、ユニオンの含まれていない単純なクエリのクエリを表し
プライマリを:最も外側の複雑なクエリを選択する
サブクエリ:select句のサブクエリは、
派生:サブクエリ句から
SQL:
set session optimizer_switch='derived_merge=off';
-- 关闭 mysql 5.7 新特性对衍生表的合并优化
EXPLAIN SELECT
( SELECT 1 FROM pea_goods WHERE pea_goods_id = 190919182516001 )
FROM
( SELECT * FROM pea_customer WHERE pea_customer_id = 190919175230001 ) goods
主、サブクエリと派生型の比較>>出力:
ID | SELECT_TYPE | テーブル | パーティション | タイプ | possible_keys | キー | key_lenに | REF | 行 | フィルタ | エクストラ |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | PRIMARY |
|
システム | 1 | 100.00 | ||||||
3 | 派生 | pea_customer | CONST | PRIMARY | PRIMARY | 8 | CONST | 1 | 100.00 | ||
2 | SUBQUERY | pea_goods | CONST | PRIMARY | PRIMARY | 8 | CONST | 1 | 100.00 |
組合:2回目以降の組合を選択
出力:
ID | SELECT_TYPE | テーブル | パーティション | タイプ | possible_keys | キー | key_lenに | REF | 行 | フィルタ | エクストラ |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | PRIMARY | 使用されませんテーブル | |||||||||
1 | 連合 |
3.テーブルの列
この列はxeplain行がテーブルにアクセスして表します。
サブクエリが存在する、テーブルの列から
組合がある場合、UNIONは、テーブルの列の値を結果1 <組合1,2>及び2は組合と選択IDの関与を示しています。
4.型の列
会合またはアクセスタイプのタイプは、MySQLでこの列のショーは、テーブルの行を見つけ、データの行のおおよその範囲を見つける方法を決定します。
最高から最悪にシステム>のconst>でeq_ref> ref>を rangge>インデックス> ALLは、 一般的には:好ましくは最大refに、クエリがレベル範囲に達したことを確認する必要があり
NULL:MySQLは、実装フェーズでアクセスまたはインデックスを持っていない、最適化の段階で問い合わせることができます。
CONST、システム:MySQLは一定にそれをクエリの一部のために最適化と変換することができる
最大1個の一致する行で、一回読み取るように、すべての列と一定の主キーまたはテーブルの一意のキーと比較した場合(表示の警告) 、より速く。constのシステムは特殊なケースです。ときにシステムという名前の1つのリストだけマッチングタプル。
5. possible_keys列
この列が表示され、それらを見つけるためにインデックスを使用することができ
possible_keys列を説明し、NULLに表示キー、この状況は表内の多くのデータはないため、このクエリ少し助け指数を考えるMYSQL、テーブル全体を選択するがある場合に発生する可能性がありますことをお問い合わせ
列はNULL、無関連の指標である場合。あなたは、クエリのパフォーマンスを向上させるために、適切なインデックスを作成することができますこの場合、あなたは、where句をチェックして結果を表示するために説明し、見ることができます
6 key 列
このインデックスのMySQLが実際に、テーブルへの最適化アクセスに使用される列が示す
クエリでpossible_keyの列インデックス、使用force_index、ignore_indexを強制または無視したい場合は、インデックスは、NULLとして使用されていない場合。
7.key_len列
この列が示すインデックスMySQLで使用されるバイト数は、その特定のインデックス列の数で計算することができます。
pea_goods_IdのBIGINTタイプとは8バイトです。
主キーのクエリ:
explain select * from pea_goods where pea_goods_Id=190919181614003;
出力
ID | SELECT_TYPE | テーブル | パーティション | タイプ | possible_keys | キー | key_lenに | REF | 行 | フィルタ | エクストラ |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | pea_goods | CONST | PRIMARY | PRIMARY | 8 | CONST | 1 | 100.00 |
key_lenに、以下の計算規則:
弦
- CHAR(N):Nバイト長
- VARCHAR(N):2バイト文字列の長さストレージ、それがUTF-8である場合、長さ3N + 2
数値型
- TINYINT:1つのバイト
- SMALLINT:2つのバイト
- INT:4つのバイト
- BIGINT:8つのバイト
タイムタイプ
- 日付:3つのバイト
- タイムスタンプ:4つのバイト
- 日時:8バイトのフィールドNULLは、記録する必要がNULLバイト1であれば可能に
8 REF列
この列が示す共通に使用されるテーブルルックアップ列の値または定数のレコードのインデックスキー列は、次のとおりCONST(定数)、フィールド名(例:film.id)
9行列
この列には、読むべきであり、これは結果セット内の行の数ではないことを検出、ノートの行数を見積もるのMySQL
10エクストラコラム
この列が示す追加情報は、以下の重要な共通を参照してください。
-
インデックスは使用:使用カバーインデックス
-
どこを使用:使用結果に声明、非インデックス列カバークエリを
-
条件条件のインデックスが使用するクエリの列が完全に条件が先頭列の範囲ですインデックスによってカバーされていません。
-
一時は使用:MySQLは、クエリを処理するために一時テーブルを作成する必要があり、このような状況は、最適化することが一般的である、最適化するために、インデックスを使用しての最初に考えました。
- 一時テーブルを作成するには、この時点で何のインデックスをgoods_nameありません
pea_goodsからSELECT DISTINCT goods_nameをEXPLAIN
いいえインデックス結果がありません
ID | SELECT_TYPE | テーブル | パーティション | タイプ | possible_keys | キー | key_lenに | REF | 行 | フィルタ | エクストラ |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | PRIMARY | pea_goods | ALL | Using temporary |
EXPLAIN SELECT DISTINCT goods_name from pea_goods
有索引结果
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | PRIMARY | pea_goods | ALL | Using index |
- Using filesort
将用外部排序而不是索引排序,数据较小时从内存排序,否则需要在磁盘完成排序。这种情况下一般也是要考虑使用索引来优化的
- goods_name 未创建索引,pea_goods,保存排序关键字pea_goods和对应的id,然后排序pea_goods并检索行记录
EXPLAIN SELECT * from pea_goods ORDER BY goods_name
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | PRIMARY | pea_goods | ALL | Using filesort |
- goods_name 创建索引
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | PRIMARY | pea_goods | ALL | Using index |
- Select tables optimized away :
使用某些聚合函数(比如 max、min)来访问存在索引的某个字段是
EXPLAIN SELECT min(pea_goods_id) from pea_goods ORDER BY goods_name
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | Select tables optimized away |
索引最佳实践
创建表
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 ( 'LiLei',22,'manager',NOW());
INSERT INTO employees ( NAME, age, position, hire_time ) VALUES ('HanMeimei', 23, 'dev', NOW());
INSERT INTO employees ( NAME, age, position, hire_time ) VALUES ('Lucy',23, 'dev', NOW());
1.全值匹配
1. EXPLAIN SELECT * FROM employees WHERE NAME = 'LiLei';
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | Extra |
---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | employees | ref | idx_name_age_position | idx_name_age_position | 74 | const | 1 |
EXPLAIN SELECT * FROM employees WHERE
NAME = 'LiLei' AND age = 22;
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | Extra |
---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | employees | ref | idx_name_age_position | idx_name_age_position | 78 | const,const | 1 |
EXPLAIN SELECT * FROM employees WHERE NAME = 'LiLei' AND age = 22
ANDposition = 'manager';
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | Extra |
---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | employees | ref | idx_name_age_position | idx_name_age_position | 140 | const,const,const | 1 |
2.最左前缀法则
如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。
EXPLAIN SELECT * FROM employees WHERE age = 22 AND position ='manager';
结果
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | employees | 3 | 33.33 | Using where |
EXPLAIN SELECT * FROM employees WHERE position = 'manager';
结果
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | employees | 3 | 33.33 | Using where |
EXPLAIN SELECT * FROM employees WHERE name = 'LiLei';
结果
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | employees | ref | idx_name_age_position | idx_name_age_position | 74 | const | 1 | 100.00 |
3.不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
EXPLAIN SELECT * FROM employees WHERE name ='LiLei';EXPLAIN SELECT * FROM employees
WHERE left(name,3) = 'LiLei';
结果
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | employees | ref | idx_name_age_position | idx_name_age_position | 74 | const | 1 | 100.00 |
给hire_time增加一个普通索引:
ALTER TABLE `employees`2ADD INDEX `idx_hire_time` (`hire_time`) USING BTREE ;
EXPLAIN select * from employees where date(hire_time) ='2018-09-30';
结果
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | employees | 3 | 100.00 | Using where |
转化为日期范围查询,会走索引:
EXPLAIN select * from employees where hire_time >='2018-09-30 00:00:00'
andhire_time <='2018-09-30 23:59:59';
还原最初索引状态
ALTER TABLE `employees` DROP INDEX `idx_hire_time`;
4.存储引擎不能使用索引中范围条件右边的列
EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age = 22 AND position ='manager';
EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age > 22 AND position ='manager';
5. 尽量使用覆盖索引(只访问索引的查询(索引列包含查询列)),减少select *语句
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在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
EXPLAIN SELECT * FROM employees WHERE name != 'LiLei';
7. is null,is not null 也无法使用索引
EXPLAIN SELECT * FROM employees WHERE name is null
8. like以通配符开头('$abc...')mysql索引失效会变成全表扫描操作
EXPLAIN SELECT * FROM employees WHERE name like '%Lei'
EXPLAIN SELECT * FROM employees WHERE name like 'Lei%'
问题:解决like'%字符串%'索引不被使用的方法?
a)使用覆盖索引,查询字段必须是建立覆盖索引字段
EXPLAIN SELECT name,age,position FROM employees WHERE name like '%Lei%';
b)如果不能使用覆盖索引则可能需要借助搜索引擎
9.字符串不加单引号索引失效
EXPLAIN SELECT * FROM employees WHERE name = '1000';
EXPLAIN SELECT * FROM employees WHERE name = 1000;
10.少用or或in
用它查询时,mysql不一定使用索引,mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引,详见范围查询优化
EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' or name = 'HanMeimei';
11.范围查询优化给年龄添加单值索引
ALTER TABLE `employees`2ADD 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;
还原最初索引状态
1ALTER TABLE employees
2DROP INDEX idx_age
;
索引使用总结:
假设 index(a,b,c)
where 语句 | 索引是否被使用 |
---|---|
where a=3 | Y ,使用到a |
where a =3 and b =4 | Y ,使用到 a,b |
where a =3 and b =4 and c=5 | Y ,使用到 a,b,c |
where b=3 / where b= 3 and c=4 /where c=5 | N |
where a=3 and c=5 | 使用到a ,但是c不可以, b中间断了 |
where a=3 and b like 'KK%' and c=4 | Y ,使用到a,b,c |
where a=3 and b like '%KK' and c =4 | Y 只用到a |
where a =3 and b like '%KK%' and c=4 | Y 只用到a |
where a =3 and b like 'K%KK%' and c=4 | Y 用到a,b,c |
like KK%相当于=常量,%KK和%KK% 相当于范围