mysql explain的type的

導語

  不少狀況下,有不少人用各類select語句查詢到了他們想要的數據後,每每便覺得工做圓滿結束了。
這些事情每每發生在一些學生亦或剛入職場但以前又沒有很好數據庫基礎的小白身上,但所謂聞道有前後,只要咱們小白好好學習,每天向上,仍是很靠譜的。mysql

  當一個sql查詢語句被寫出來以後,其實你的工做只完成了一小半,接下來更重要的工做是評估你本身寫的sql的質量與效率。mysql爲咱們提供了頗有用的輔助武器explain,它向咱們展現了mysql接收到一條sql語句的執行計劃。根據explain返回的結果咱們即可以知道咱們的sql寫的怎麼樣,是否會形成查詢瓶頸,同時根據結果不斷的修改調整查詢語句,從而完成sql優化的過程。sql

  雖然 explain返回的結果項不少,這裏咱們只關注三種,分別是type,key,rows。其中key代表的是此次查找中所用到的索引,rows是指此次查找數據所掃描的行數(這裏能夠先這樣理解,但其實是內循環的次數)。而type則是本文要詳細記錄的鏈接類型,前兩項重要並且簡單,無需多說。數據庫

type -- 鏈接類型

  type意味着類型,這裏的type官方全稱是「join type」,意思是「鏈接類型」,這樣很容易給人一種錯覺以爲必須須要倆個表以上纔有鏈接類型。事實上這裏的鏈接類型並不是字面那樣的狹隘,它更確切的說是一種數據庫引擎查找表的一種方式,在《高性能mysql》一書中做者更是以爲稱呼它爲訪問類型更貼切一些。mysql優化

  mysql5.7中type的類型達到了14種之多,這裏只記錄和理解最重要且常常碰見的六種類型,它們分別是all,index,range,ref,eq_ref,const。從左到右,它們的效率依次是加強的。撇開sql的具體應用環境以及其餘因素,你應當儘可能優化你的sql語句,使它的type儘可能靠右,但實際運用中仍是要綜合考慮各個方面的。性能

接下來,爲了演示和重現這幾種鏈接類型,我新建了一個數據測試表,以方面更好的理解這五種類型。學習

| employee | CREATE TABLE `employee` (
  `rec_id` int(11) NOT NULL AUTO_INCREMENT,
  `no` varchar(10) NOT NULL,
  `name` varchar(20) NOT NULL,
  `position` varchar(20) NOT NULL,
  `age` varchar(2) NOT NULL,
  PRIMARY KEY (`rec_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 |

all(重要

  這即是所謂的「全表掃描」,若是是展現一個數據表中的所有數據項,卻是以爲也沒什麼,若是是在一個查找數據項的sql中出現了all類型,那一般意味着你的sql語句處於一種最原生的狀態,有很大的優化空間。
爲何這麼說呢?由於all是一種很是暴力和原始的查找方法,很是的耗時並且低效。用all去查找數據就比如這樣的一個情形:S學校有倆萬人,我告訴你你給我找到小明,而後你怎麼作呢!你固然是把全校倆萬人挨個找一遍,即便你很幸運第一我的便找到了小明,可是你仍然不能停下,由於你沒法確認是否有另一個小明存在,直到你把倆萬人找完爲止。因此,基本全部狀況,咱們都要避免這樣類型的查找,除非你不得不這樣作。
以employee表爲例,下面一種情形即是all類型的查找:測試

mysql> explain select * from employee where `no` = '20150001';
+----+-------------+----------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+----------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | employee | ALL  | NULL          | NULL | NULL    | NULL |    5 | Using where |
+----+-------------+----------+------+---------------+------+---------+------+------+-------------+

這是由於no列既不是主鍵也不是索引,所以只能採用全表掃描來查找目標no。優化

index(重要

  這種鏈接類型只是另一種形式的全表掃描,只不過它的掃描順序是按照索引的順序。這種掃描根據索引而後回表取數據,和all相比,他們都是取得了全表的數據,並且index要先讀索引並且要回表隨機取數據,所以index不可能會比all快(取同一個表數據),但爲何官方的手冊將它的效率說的比all好,惟一可能的緣由在於,按照索引掃描全表的數據是有序的。這樣一來,結果不一樣,也就無法比效率的問題了。
若是必定要比效率,只須要獲取這個表的數據而且排序即可以看出來誰比誰效率高了:spa

mysql> explain select * from employee order by `no` ;
+----+-------------+----------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+----------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | employee | ALL  | NULL          | NULL | NULL    | NULL |    5 | Using filesort |
+----+-------------+----------+------+---------------+------+---------+------+------+----------------+
mysql> explain select * from employee order by rec_id ;
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------+
| id | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows | Extra |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------+
|  1 | SIMPLE      | employee | index | NULL          | PRIMARY | 4       | NULL |    5 | NULL  |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------+

  上面能夠看出,根據no列排序的鏈接類型是all型的,可是注意extra列是用到了排序(Using filesort),而根據rec_id列排序的鏈接類型是index,並且獲得的結果天然是有序的,不準額外的排序。可能正是由於這個緣故,index的效率比all高,但注意這須要相同的條件才成立(既須要排序)。操作系統

  若是鏈接類型爲type,並且extra列中的值爲‘Using index’,那麼稱這種狀況爲 索引覆蓋
  索引覆蓋意味着什麼呢?想象這樣一種場景,若是說一本新華字典是一張表,固然前面的索引部分(假設按照部首的索引)是這張表的索引,那麼索引覆蓋就至關於根據部首索引獲取第一個字到最後一個字(新華字典的全部字)。咱們得到了字典中全部的字,然而咱們並無查一次表,由於咱們想要的都早索引中,即索引覆蓋。

mysql> explain select rec_id from employee ;
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | employee | index | NULL          | PRIMARY | 4       | NULL |    5 | Using index |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+

  上例獲取的rec_id恰好爲索引列,所以無需回表取數據。

range(重要

  ange指的是有範圍的索引掃描,相對於index的全索引掃描,它有範圍限制,所以要優於index。關於range比較容易理解,須要記住的是出現了range,則必定是基於索引的。同時除了顯而易見的between,and以及'>','<'外,in和or也是索引範圍掃描。

ref(重要

  出現該鏈接類型的條件是: 查找條件列使用了索引並且不爲主鍵和unique。其實,意思就是雖然使用了索引,但該索引列的值並不惟一,有重複。這樣即便使用索引快速查找到了第一條數據,仍然不能中止,要進行目標值附近的小範圍掃描。但它的好處是它並不須要掃全表,由於索引是有序的,即使有重複值,也是在一個很是小的範圍內掃描。下面爲了演示這種情形,給employee表中的name列添加一個普通的key(值容許重複)

 alter table employee add key I_EMPLOYEE_NAME(`name`); 

  接下來,在employee表中根據name查找數據的時候,mysql優化器便選擇了ref的鏈接類型。

mysql> explain select * from employee where `name` = '張三';
+----+-------------+----------+------+----------------+----------------+---------+-------+------+-----------------------+
| id | select_type | table    | type | possible_keys  | key            | key_len | ref   | rows | Extra                 |
+----+-------------+----------+------+----------------+----------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | employee | ref  | I_EMPLOYEE_NAM | I_EMPLOYEE_NAM | 62      | const |    1 | Using index condition |
+----+-------------+----------+------+----------------+----------------+---------+-------+------+-----------------------+

ref_eq(重要

  ref_eq 與 ref相比牛的地方是,它知道這種類型的查找結果集只有一個?什麼狀況下結果集只有一個呢!那即是使用了主鍵或者惟一性索引進行查找的狀況,好比根據學號查找某一學校的一名同窗,在沒有查找前咱們就知道結果必定只有一個,因此當咱們首次查找到這個學號,便當即中止了查詢。這種鏈接類型每次都進行着精確查詢,無需過多的掃描,所以查找效率更高,固然列的惟一性是須要根據實際狀況決定的。
  在單個表中,曾嘗試了不少方法想出現ref_eq的鏈接類型,然而不少時候出現的都是const,所以不得不隨手鍊接了一張表獲得了想要的鏈接類型,該表的建表代買爲。(博主比較懶,鏈接了兩個沒有關係的表,o(╯□╰)o)

CREATE TABLE `score` (
  `rec_id` INT(11) NOT NULL AUTO_INCREMENT,
  `stu_id` INT(11) NOT NULL,
  `mark` INT(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`rec_id`),
  UNIQUE KEY `UK_SCORE_STU_ID` (`stu_id`)
) ENGINE=INNODB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8

  employee表中有五條數據,score表中有對應的五條數據,其中employee的rec_id 和score的stu_id 是一一對應的。

mysql> explain select ep.name,sc.mark from employee ep,score sc where ep.rec_id = sc.stu_id;
+----+-------------+-------+--------+-----------------+---------+---------+-----------------+------+-------+
| id | select_type | table | type   | possible_keys   | key     | key_len | ref             | rows | Extra |
+----+-------------+-------+--------+-----------------+---------+---------+-----------------+------+-------+
|  1 | SIMPLE      | sc    | ALL    | UK_SCORE_STU_ID | NULL    | NULL    | NULL            |    5 | NULL  |
|  1 | SIMPLE      | ep    | eq_ref | PRIMARY         | PRIMARY | 4       | my_db.sc.stu_id |    1 | NULL  |
+----+-------------+-------+--------+-----------------+---------+---------+-----------------+------+-------+

  上面就能夠看到score表是全表掃描的類型,rows=5表明外層表循環了五次(由於有五條數據),可是employee表的rows怎麼是1,怎麼可能?剛開始也是很疑惑,這與mysql的查詢原理息息相關,rows實際反映的是查詢的內循環數,針對外層的每一條數據匹配,employee的確一槍就能夠命中,所以rows爲1。

const

  一般狀況下,若是將一個主鍵放置到where後面做爲條件查詢,mysql優化器就能把此次查詢優化轉化爲一個常量。至於如何轉化以及什麼時候轉化,這個取決於優化器。

總結

  explain 就像一面鏡子,有事沒事寫完sql記得explain一下。同時,在寫文章也發現,有不少東西和細節,想要明白清楚,也是沒有那麼簡單的,須要對操做系統以及數據庫的底層查詢和運行原理要有一個清楚的理解。同時type的幾種類型幾乎都是基於索引之上的,所以須要對索引有個深刻的瞭解,並且explain的結果能夠指導咱們何時加索引,何時不加索引,從而讓咱們更好的使用索引。

結尾

  轉載自:https://blog.csdn.net/dennis211/article/details/78170079

相關文章
相關標籤/搜索