使用explain關鍵字可以模擬優(yōu)化器執(zhí)行sql語句,從而知道m(xù)ysql是如何處理sql語句的,分析你的查詢語句或者是結構性能。
我們通過幾張表來使用explain的例子:
在select語句之前增加explain關鍵字,MySQL會在查詢的基礎上設置一個標記,執(zhí)行查詢時,會返回執(zhí)行計劃的信息,而不是執(zhí)行這條sql語句(如果from中包含子查詢,仍會執(zhí)行該子查詢的,將結果放入臨時表中)
使用的的表
1.actor表(不設置索引,存儲引擎是InnoDB)
2.film表(設置name索引(輔助索引),存儲引擎是InnoDB)
3.film-actor表(設置聯(lián)合輔助索引(film_id,actor_id))
使用的表我們建立完畢了,那接下來就讓我們熟悉一下explain的始終和返回執(zhí)行計劃中字段的含義
mysql>EXPLAIN SELECT (SELECT 1 from actor ) from film;
在查詢中的每一個表會輸出一行,如果有兩個表通過join連接查詢,那么會輸出兩行,表的意思相當廣泛:可以是子查詢,一個union結果等。
explain有兩個輔助寫法:
1)explain extended:會在explain的基礎上額外提供一些查詢的優(yōu)化的信息,緊隨其后的通過show warnings 命令可以的到優(yōu)化后的查詢語句,從而看出優(yōu)化器優(yōu)化了什么,二外有filtered列,是一個半分比的值,rows*filtered/100可以估算出來將要和explain中前一個表進行連接的行數(shù)(前一個表指的是explain中的id值比當前id值小的表)
mysql>EXPLAIN EXTENDED SELECT * from film where id = 1;
mysql>show WARNINGS;(Note 得到優(yōu)化后的語句)
2)explain partitions :相比explain多了個partition字段,如果查詢是基于分表的的話,會顯示查詢將訪問的分區(qū)
那接下來我們介紹一下explain中每個列的信息
1.id列
id列的編號是select的序列號,有幾個select就有幾個id,并且id的順序是按select出現(xiàn)的順序增長的。MySQL將 select查詢分為簡單查詢(SIMPLE)和復雜查詢(PRIMARY)。
復雜查詢分為三類:簡單子查詢、派生表(from語句中的子查詢) 、union查詢。
id列越大執(zhí)行優(yōu)先級越高,id相同則從上往下執(zhí)行,id為NULL最后執(zhí)行1)簡單子查詢
1)簡單子查詢
mysql>EXPLAIN SELECT (SELECT 1 FROM actor LIMIT 1) FROM film;
2)from字句中的子查詢(在mysql5.7的版中沒有派生表的返回,具體原因不明,可能是5.7的優(yōu)化了)
mysql>EXPLAIN SELECT id FROM (SELECT id FROM film) f;
這個查詢執(zhí)行時有個臨時表別名為f,外部的select查詢就是引用了這個臨時表
3)union查詢
mysql>EXPLAIN select 1 from film union all select 1 from actor;
union 結果總是放在一個匿名的臨時表中,臨時表不在sql中出現(xiàn),因此他的id是null
2.select_type列
select_type表示對應行是簡單的還是復雜的查詢,如果是復雜的查詢,又是上述三種哪一種
1)simple:簡單查詢,查詢不包含子查詢和union
mysql>explain select * from film where id =1;
2)primary:復雜查詢中的最外層select
3)subquery:包含select中的子查詢(不在from子句中)
4)derived:包含在from子句中的子查詢,mysql會將結果存放在一個臨時表中,也稱派生表(derived的英文義)
用這個例子來解析primary,subquery,derived類型
mysql>explain select (select 1 from actor where id = 1) from (select * from film where id = 1) der;
5)union :在union中的第二和隨后的select
6)union result:從union臨時表檢索結果的select
用這個例子來了解union和union result 類型:
mysql> explain select 1 from film union select 1 from film ;
3.table 列
這一列表示explain的一行正在訪問哪個表。
當from子句中有子查詢的時候,table列是<derivenN>格式,表示當前查詢依賴id=N的查詢,于是先執(zhí)行id=N的查詢。
當有union時,union result的table列的值為<union1,2>,1和2表示參與的select行id
4.type列
這一列表示關聯(lián)類型和訪問類型,即MySQL決定如何查找表中的行,查找的數(shù)據(jù)記錄的大概范圍。
依次從最優(yōu)到最差分別為:system>const>eq_ref>ref>range>index>all(常用的)
一般來說,得到保證查詢打到range級別,最好打到ref級別
NULL:MySQL能夠在優(yōu)化階段分解查詢語句,在執(zhí)行階段用不著再訪問表或索引。例如再索引列中選區(qū)最小值,可以單獨擦查找索引來完成,不需要再執(zhí)行時訪問表。
mysql>explain select min(id) from film;
注意:mysql中主鍵就是索引,mysql會幫主鍵主動建立索引的
const,system:mysql能對查詢的某部分進行優(yōu)化并將其轉化成一個常量(可以看著show warnings的結果)。用于primary_key和union key的所有列和常數(shù)比較的時候,所以表最多一個匹配行,讀一次,速度比較快,system時const的特例,表中只有一條元組數(shù)據(jù)匹配就是system
mysql>explain extended select * from (select * from film where id = 1) f;
注意:const就是查詢條件時主鍵或者唯一性約束的,查詢出來的就只有一條數(shù)據(jù)的,而system查詢的表就只有一條數(shù)據(jù)的。
mysql>show warnings;(優(yōu)化后的sql)
eq_ref:primary_key或者union key索引的所有部分被連接使用,最多返回一條符合條件的記錄,這可能再const之外最好的連接類型了,簡單的select查詢就不會出現(xiàn)這種類型
mysql>explain select * from film_actor left join film on film_id = film.id ;
注意:film_actor也是使用索引查詢的原因時覆蓋索引(后面有說)
ref相比eq. ret,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前綴,索引要和某個值相比較,可能會找到多個符合條件的行。
1.簡單select查詢,name是普通索引(非唯一索引)
mysql>explain select * from film where name = "film1";
2.關聯(lián)表查詢,idx_ film_ actor_ id是film_ 1d和actor_ id的聯(lián)合索引,這里使用到了film_ actor的左邊前綴film_id部分。
mysql> explain select * from film LEFT JOIN film_actor on film.id = film_actor.film_id;
range:范圍掃描通常出現(xiàn)在in(), between,> <, >=等操作中。使用一個索引來檢索給定范圍的行。
mysql>explain select * from actor WHERE id > 1
index:掃描全表索引,這通常比all快一些,(index是從索引中讀取的,而all是從硬盤中讀?。?/p>
mysql>explain select * from film ;
ALL:即全表掃描,意味著mysql需要從頭到尾去查詢所需要的好行,通常情況下這需要增加索引來進行優(yōu)化
mysql>explain select * from actor;
5.possible_keys列
這一列顯示查詢可能使用哪些索引來查找。
explain時可能出現(xiàn)posible. keys有列,而key顯示NULL的情況,這種情況是因為表中數(shù)據(jù)不多, mysq認為索引對此查詢幫助不大,選擇了全表查詢。
如果該列是NULL,則沒有相關的素引,在這種情況下,可以通過檢查where子句看是否可以創(chuàng)造一個適當?shù)乃饕齺硖岣卟樵冃阅?,然后用explain查看效果。
6.key列
這一列是示mysq實際采用索引來優(yōu)化対該表的訪問
如果沒有使用索引,則垓列是NULL.如果想強制mysq使用或忽視possible. keys列中的索引,在査洵中使用force index. ignore index。
7.key_len列
這一列顯示了mysql在索引里使用的字節(jié)數(shù),通過這個值可以算出具體使用了索引中的哪些列。
舉例來說,film_actor的聯(lián)合索引 idx_film_actor_id 由 film_id 和 actor_id 兩個int列組成,并且每個int是4字節(jié)。通過結果中的key_len=4可推斷出查詢使用了第一個列:film_id列來執(zhí)行索引查找。
mysql>explain select * from film_actor where film_id = 2;
key_len計算規(guī)則如下
字符串
char(n):n字節(jié)長度
varchar(n):2字節(jié)存儲字符串長度,如果是utf-8,則長度 3n + 2
數(shù)值類型
tinyint:1字節(jié)
smallint:2字節(jié)
int:4字節(jié)
bigint:8字節(jié)
時間類型
date:3字節(jié)
timestamp:4字節(jié)
datetime:8字節(jié)
如果字段允許為 NULL,需要1字節(jié)記錄是否為 NULL
索引最大長度是768字節(jié),當字符串過長時,mysql會做一個類似左前綴索引的處理,將前半部分的字符提取出來做索引。
8. ref列
這一列顯示了在key列記錄的索引中,表查找值所用到的列或常量,常見的有: const (常量) ,字段名(例: film.id)
9. rows列
這一列是mysq估計要讀取并檢測的行數(shù),注意這個不是結果集里的行數(shù)。
10. Extra列
這一列展示的是額外信息。常見的重要值如下:
Using index: 查詢的列被索引覆蓋,并且where篩選條件是索引的前導列,是性能高的表現(xiàn)。一般是使用了覆蓋索引索引包含了所有查詢的字段)。對于innodb來說,如果是輔助索引性能會有不少提高.
mysql>explain select film_id from film_actor where film_id = 1;
Using where : 查詢的列未被索引覆蓋 where篩選條件非索引的前導列
mysql>explain select * from actor where name = 'a';
Using where Using index: 查詢的列被索引覆蓋,并且where篩選條件是索引列之一但是不是索引的前導列,意味著無法直接通過索引查找來查詢到符合條件的數(shù)據(jù)
mysql> explain select film_id from film_actor where actor_id = 1;
NULL:查詢的列未被索引覆蓋,并且where篩選條件是索引的前導列,意味著用到了索引,但是部分字段未被索引覆蓋,必須通過“回表“來實現(xiàn),不是純粹地用到了索引,也不是完全沒用到索引.
Using temporary: mysql需要創(chuàng)建一張臨時表來處理査洵. 出現(xiàn)這種情況一般是要迸行優(yōu)化的, 首先是想到用索引來優(yōu)化.
1. actor.name沒有索引,此時創(chuàng)建了張臨時表來dstinct.
mysql>explain select distinct name from actor;
2.film.name 建立了idx_name索引,此時查詢時extra時using index,沒有使用臨時表
mysql>explain select distinct name from film;
Using filesort: mysql會對結果使用一個外部索引排序,而不是按索引次序從表里讀取行。此時mysql會根據(jù)連接類型瀏覽所有符合條件的記錄,井保存排序關鍵字和指針,然后排序關鍵字并按順序檢索行信息。這種情況下一般也是要考慮使用索引來優(yōu)化的。
1. actor .name未創(chuàng)建索引,會瀏覽actor整個表, 保存排序關鍵字name和對那個的id,然后排序name并檢索記錄
mysql>explain select * from actor order by name;
2.film.name 建立了idx_name索引,此時查詢時extra時using index
使用的表
- 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. 全值匹配
EXPLAIN SELECT * FROM employees WHERE name= 'LiLei';
EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age = 22;
EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' AND age = 22 AND position ='manager';
2.最佳左前綴法則
如果索引了多列,要遵守最左前綴法則。指的是查詢從索引的最左前列開始并且不跳過索引中的列。
EXPLAIN SELECT * FROM employees WHERE age = 22 AND position ='manager';
EXPLAIN SELECT * FROM employees WHERE position = 'manager';
EXPLAIN SELECT * FROM employees WHERE name = 'LiLei';
3.不在索引列上做任何操作(計算、函數(shù)、(自動or手動)類型轉換),會導致索引失效而轉向全表掃描
EXPLAIN SELECT * FROM employees WHERE name = 'LiLei';
EXPLAIN SELECT * FROM employees WHERE left(name,3) = 'LiLei';
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)當覆蓋索引指向的字段是varchar(380)及380以上的字段時,覆蓋索引會失效!
9.字符串不加單引號索引失效
EXPLAIN SELECT * FROM employees WHERE name = '1000';
EXPLAIN SELECT * FROM employees WHERE name = 1000;
10.少用or,用它連接時很多情況下索引會失效
EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' or name = 'HanMeimei';
總結:
like KK%相當于=常量,%KK和%KK% 相當于范圍
聯(lián)系客服