explain都不懂,還說會SQL調優?

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中測試庫的表,需要的同學可以自行去下載。官方文檔: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_no  from 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_emp  where 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_no  from dept_emp;")和sqlB("select from_date from dept_emp where dept_no > 'd005';"),這個時候sqlB根據where條件掃描索引樹後,需要回表查詢相應的行數據,以獲取from_date的值,而sqlA雖然掃描了整顆索引樹,但並不需要回表,所以速度可能會比sqlB更快。

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:建立了臨時表來保存中間結果,查詢完成之後又要把臨時表刪除。會很影響性能,需儘快優化。

有時在extra字段中會出現"Impossible WHERE noticed after reading const tables"這種描述。翻看網上資料後,個人發現這是mysql一種很怪的處理方式。

當sql語句滿足:

1、根據主鍵查詢或者唯一性索引查詢;

2、where操作符爲"="時。

在sql語句優化階段,mysql會先根據查詢條件找到相關記錄,這樣,如果這條數據不存在,實際上就進行了一次全掃描,然後得出一個結論,該數據不在表中。這樣對於併發較高的數據庫,會加大負載。所以,如果數據不用唯一的話,普通的索引比唯一索引更好用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章