mysql索引篇之explain命令介紹
mysql中的explain命令可以用來檢視sql語句是否使用了索引,用了什麼索引,有沒有做全表掃描。可以幫助我們優化查詢語句。
explain出來的資訊有10列,文章主要介紹type、key、Extra這幾個欄位。
演示中涉及到的表結構如下:
CREATE TABLE `dept_desc` ( `dept_no` char(4) NOT NULL, `dept_name` varchar(40) NOT NULL, `desc` varchar(255) NOT NULL, PRIMARY KEY (`dept_no`) ) ENGINE=InnoDB CREATE TABLE `dept_emp` ( `emp_no` int(11) NOT NULL, `dept_no` char(4) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`dept_no`), KEY `dept_no` (`dept_no`), CONSTRAINT `dept_emp_ibfk_1` FOREIGN KEY (`emp_no`) REFERENCES `employees` (`emp_no`) ON DELETE CASCADE, CONSTRAINT `dept_emp_ibfk_2` FOREIGN KEY (`dept_no`) REFERENCES `departments` (`dept_no`) ON DELETE CASCADE ) ENGINE=InnoDB CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` enum('M','F') NOT NULL, `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`) ) ENGINE=InnoDB
上面的表都是mysql中測試庫的表,需要的同學可以自行去下載。
官方文件:ofollow,noindex">https://dev.mysql.com/doc/employee/en/employees-installation.html ;
github下載地址:https://github.com/datacharmer/test_db.git
key
sql語句實際執行時使用的索引列,有時候mysql可能會選擇優化效果不是最好的索引,這時,我們可以在select語句中使用force index(INDEXNAME)來強制mysql使用指定索引或使用ignore index(INDEXNAME)強制mysql忽略指定索引
type
訪問型別,表示資料庫引擎查詢表的方式,常見的type型別有:all,index,range,ref,eq_ref,const。
-
all
全表掃描,表示sql語句會把表中所有表資料全部讀取讀取掃描一遍。效率最低,我們應儘量避免。
mysql> explain select * from dept_emp; +----+-------------+----------+------+---------------+------+---------+------+--------+-------+ | id | select_type | table| type | possible_keys | key| key_len | ref| rows| Extra | +----+-------------+----------+------+---------------+------+---------+------+--------+-------+ |1 | SIMPLE| dept_emp | ALL| NULL| NULL | NULL| NULL | 331570 | NULL| +----+-------------+----------+------+---------------+------+---------+------+--------+-------+
-
index
全索引掃描,表示sql語句將會把整顆二級索引樹全部讀取掃描一遍,因為二級索引樹的資料量比全表資料量小,所以效率比all高一些。一般查詢語句中查詢欄位為索引欄位,且無where子句時,type會為index。如下,mysql確定使用dept_no這個索引,然後掃描整個dept_no索引樹得到結果。
mysql> explain select dept_nofrom dept_emp; +----+-------------+----------+-------+---------------+---------+---------+------+--------+-------------+ | id | select_type | table| type| possible_keys | key| key_len | ref| rows| Extra| +----+-------------+----------+-------+---------------+---------+---------+------+--------+-------------+ |1 | SIMPLE| dept_emp | index | NULL| dept_no | 4| NULL | 331570 | Using index | +----+-------------+----------+-------+---------------+---------+---------+------+--------+-------------+
-
range
部分索引掃描,當查詢為區間查詢,且查詢欄位為索引欄位時,這時會根據where條件對索引進行部分掃描。
mysql> explain select * from dept_empwhere emp_no > '7'; +----+-------------+----------+-------+---------------+---------+---------+------+--------+-------------+ | id | select_type | table| type| possible_keys | key| key_len | ref| rows| Extra| +----+-------------+----------+-------+---------------+---------+---------+------+--------+-------------+ |1 | SIMPLE| dept_emp | range | PRIMARY| PRIMARY | 4| NULL | 165785 | Using where | +----+-------------+----------+-------+---------------+---------+---------+------+--------+-------------+
-
ref
出現於where操作符為‘=’,且where欄位為非唯一索引 的單表查詢或聯表查詢。
// 單表 mysql> explain select * from dept_emp where dept_no = 'd005'; +----+-------------+----------+------+---------------+---------+---------+-------+--------+-----------------------+ | id | select_type | table| type | possible_keys | key| key_len | ref| rows| Extra| +----+-------------+----------+------+---------------+---------+---------+-------+--------+-----------------------+ |1 | SIMPLE| dept_emp | ref| dept_no| dept_no | 4| const | 145708 | Using index condition | +----+-------------+----------+------+---------------+---------+---------+-------+--------+-----------------------+ // 聯表 mysql> explain select * from dept_emp,departments where dept_emp.dept_no = departments.dept_no; +----+-------------+-------------+-------+---------------+-----------+---------+-------------------------------+------+-------------+ | id | select_type | table| type| possible_keys | key| key_len | ref| rows | Extra| +----+-------------+-------------+-------+---------------+-----------+---------+-------------------------------+------+-------------+ |1 | SIMPLE| departments | index | PRIMARY| dept_name | 42| NULL|9 | Using index | |1 | SIMPLE| dept_emp| ref| dept_no| dept_no| 4| employees.departments.dept_no |1 | NULL| +----+-------------+-------------+-------+---------------+-----------+---------+-------------------------------+------+-------------+
-
eq_ref
出現於where操作符為‘=’,且where欄位為唯一索引 的聯表查詢 。
mysql> explain select * from departments,dept_desc where departments.dept_name=dept_desc.dept_name; +----+-------------+-------------+--------+---------------+-----------+---------+-------------------------------+------+-------------+ | id | select_type | table| type| possible_keys | key| key_len | ref| rows | Extra| +----+-------------+-------------+--------+---------------+-----------+---------+-------------------------------+------+-------------+ |1 | SIMPLE| dept_desc| ALL| NULL| NULL| NULL| NULL|1 | NULL| |1 | SIMPLE| departments | eq_ref | dept_name| dept_name | 42| employees.dept_desc.dept_name |1 | Using index | +----+-------------+-------------+--------+---------------+-----------+---------+-------------------------------+------+-------------+
-
const
出現於where操作符為‘=’,且where欄位為唯一索引 的單表查詢 ,此時最多隻會匹配到一行。
mysql> explain select * from departments where dept_no = 'd005'; +----+-------------+-------------+-------+---------------+---------+---------+-------+------+-------+ | id | select_type | table| type| possible_keys | key| key_len | ref| rows | Extra | +----+-------------+-------------+-------+---------------+---------+---------+-------+------+-------+ |1 | SIMPLE| departments | const | PRIMARY| PRIMARY | 4| const |1 | NULL| +----+-------------+-------------+-------+---------------+---------+---------+-------+------+-------+
綜上,單從type欄位考慮效率,const > eq_ref > ref > range > index > all.
注意:我們不能僅僅根據type去判斷兩條sql的執行速度。例如type為range的查詢不一定比type為index的全表查詢速度要快,還要看具體的sql。因為type為index時,查詢是不需要回表操作的,而type為range時,有可能需要回表操作。如sqlA("select dept_nofrom dept_emp;")和sqlB("select from_date from dept_emp where dept_no > 'd005';"),這個時候sqlB根據where條件掃描索引樹後,需要回表查詢相應的行資料,以獲取from_date的值,而sqlA雖然掃描了整顆索引樹,但並不需要回表,所以速度可能會比sqlB更快。
回表操作、索引相關可以閱讀<mysql索引初識>這篇文章
Extra
extra列會包含一些十分重要的資訊,我們可以根據這些資訊進行sql優化
using index:sql語句沒有where查詢條件,使用覆蓋索引,不需要回表查詢即可拿到結果
using where:沒有使用索引/使用了索引但需要回表查詢且沒有使用到下推索引
using index && useing where: sql語句有where查詢條件,且使用覆蓋索引,不需要回表查詢即可拿到結果。
Using index condition:使用索引查詢,sql語句的where子句查詢條件欄位均為同一索引欄位,且開啟索引下推功能,需要回表查詢即可拿到結果。
Using index condition && using where:使用索引查詢,sql語句的where子句查詢條件欄位存在非同一索引欄位,且開啟索引下推功能,需要回表查詢即可拿到結果。
using filesort: 當語句中存在order by時,且orderby欄位不是索引,這個時候mysql無法利用索引進行排序,只能用排序演算法重新進行排序,會額外消耗資源。
Using temporary:建立了臨時表來儲存中間結果,查詢完成之後又要把臨時表刪除。會很影響效能,需儘快優化。
下推索引、覆蓋索引相關介紹可閱讀<mysql索引篇之覆蓋索引、字首索引、索引下推 >這篇文章
有時在extra欄位中會出現"Impossible WHERE noticed after reading const tables"這種描述。翻看網上資料後,個人發現這是mysql一種很怪的處理方式。當sql語句滿足:
1、根據主鍵查詢或者唯一性索引查詢;
2、where操作符為"="時。
在sql語句優化階段,mysql會先根據查詢條件找到相關記錄,這樣,如果這條資料不存在,實際上就進行了一次全掃描,然後得出一個結論,該資料不在表中。這樣對於併發較高的資料庫,會加大負載。所以,如果資料不用唯一的話,普通的索引比唯一索引更好用。(文章連結:https://yq.aliyun.com/articles/393774 )