很多情況下,有很多人用各種select語句查詢到了他們想要的資料後,往往便以為工作圓滿結束了。
當乙個sql查詢語句被寫出來之後,其實你的工作只完成了一小半,接下來更重要的工作是評估你自己寫的sql的質量與效率。mysql為我們提供了很有用的輔助**explain,它向我們展示了mysql接收到一條sql語句的執行計畫。根據explain返回的結果我們便可以知道我們的sql寫的怎麼樣,是否會造成查詢瓶頸,同時根據結果不斷的修改調整查詢語句,從而完成sql優化的過程。
雖然 explain返回的結果項很多,這裡我們只關注三種,分別是type,key,rows。其中key表明的是這次查詢中所用到的索引,rows是指這次查詢資料所掃瞄的行數(這裡可以先這樣理解,但實際上是內迴圈的次數)。而type則是本文要詳細記錄的連線型別,前兩項重要而且簡單,無需多說。
system:系統表,少量資料,往往不需要進行磁碟io;
const:常量連線;
eq_ref:主鍵索引(primary key)或者非空唯一索引(unique not null)等值掃瞄;
ref:非主鍵非唯一索引等值掃瞄;
range:範圍掃瞄;
index:索引樹掃瞄;
all:全表掃瞄(full table scan);
這些是最常見的,大家去explain自己工作中的sql語句,95%都是上面這些型別。
上面各類掃瞄方式由快到慢:
system > const > eq_ref > ref > range > index > all
type意味著型別,這裡的type官方全稱是「join type」,意思是「連線型別」,這樣很容易給人一種錯覺覺得必須需要倆個表以上才有連線型別。事實上這裡的連線型別並非字面那樣的狹隘,它更確切的說是一種資料庫引擎查詢表的一種方式,在《高效能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 |
這便是所謂的「全表掃瞄」,如果是展示乙個資料表中的全部資料項,倒是覺得也沒什麼,如果是在乙個查詢資料項的sql**現了all型別,那通常意味著你的sql語句處於一種最原生的狀態,有很大的優化空間。
為什麼這麼說呢?因為all是一種非常暴力和原始的查詢方法,非常的耗時而且低效。用all去查詢資料就好比這樣的乙個情形:s學校有倆萬人,我告訴你你給我找到小明,然後你怎麼做呢!你當然是把全校倆萬人挨個找一遍,即使你很幸運第乙個人便找到了小明,但是你仍然不能停下,因為你無法確認是否有另外乙個小明存在,直到你把倆萬人找完為止。所以,基本所有情況,我們都要避免這樣型別的查詢,除非你不得不這樣做。
以employee表為例,下面一種情形便是all型別的查詢:
這是因為no列既不是主鍵也不是索引,因此只能採用全表掃瞄來查詢目標no。
這種連線型別只是另外一種形式的全表掃瞄,只不過它的掃瞄順序是按照索引的順序。這種掃瞄根據索引然後回表取資料,和all相比,他們都是取得了全表的資料,而且index要先讀索引而且要回表隨機取資料,因此index不可能會比all快(取同乙個表資料),但為什麼官方的手冊將它的效率說的比all好,唯一可能的原因在於,按照索引掃瞄全表的資料是有序的。這樣一來,結果不同,也就沒法比效率的問題了。
如果一定要比效率,只需要獲取這個表的資料並且排序便可以看出來誰比誰效率高了:
上面可以看出,根據no列排序的連線型別是all型的,但是注意extra列是用到了排序(using filesort),而根據rec_id列排序的連線型別是index,而且得到的結果自然是有序的,不許額外的排序。可能正是因為這個緣故,index的效率比all高,但注意這需要相同的條件才成立(既需要排序)。
如果連線型別為index,而且extra列中的值為『using index』,那麼稱這種情況為 索引覆蓋;
索引覆蓋意味著什麼呢?想象這樣一種場景,如果說一本新華字典是一張表,當然前面的索引部分(假設按照部首的索引)是這張表的索引,那麼索引覆蓋就相當於根據部首索引獲取第乙個字到最後乙個字(新華字典的所有字)。我們獲得了字典中所有的字,然而我們並沒有查一次表,因為我們想要的都在索引中,即索引覆蓋。
上例獲取的rec_id剛好為索引列,因此無需回表取資料。
range指的是有範圍的索引掃瞄,相對於index的全索引掃瞄,它有範圍限制,因此要優於index。關於range比較容易理解,需要記住的是出現了range,則一定是基於索引的。同時除了顯而易見的between,and以及'>','
出現該連線型別的條件是: 查詢條件列使用了索引而且不為主鍵和unique。其實,意思就是雖然使用了索引,但該索引列的值並不唯一,有重複。這樣即使使用索引快速查詢到了第一條資料,仍然不能停止,要進行目標值附近的小範圍掃瞄。但它的好處是它並不需要掃全表,因為索引是有序的,即便有重複值,也是在乙個非常小的範圍內掃瞄。下面為了演示這種情形,給employee表中的name列新增乙個普通的key(值允許重複)
alter table employee add key i_employee_name(`name`);
接下來,在employee表中根據name查詢資料的時候,mysql優化器便選擇了ref的連線型別。
ref_eq 與 ref相比牛的地方是,它知道這種型別的查詢結果集只有乙個,什麼情況下結果集只有乙個呢!那便是使用了主鍵或者唯一性索引進行查詢的情況,比如根據學號查詢某一學校的一名同學,在沒有查詢前我們就知道結果一定只有乙個,所以當我們首次查詢到這個學號,便立即停止了查詢。這種連線型別每次都進行著精確查詢,無需過多的掃瞄,因此查詢效率更高,當然列的唯一性是需要根據實際情況決定的。
在單個表中,曾嘗試了很多方法想出現ref_eq的連線型別,然而很多時候出現的都是const,因此不得不隨手連線了一張表得到了想要的連線型別,該錶的建表sql為。(博主比較懶,連線了兩個沒有關係的表,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 是一一對應的。
上面就可以看到score表是全表掃瞄的型別,rows=5代表外層表迴圈了五次(因為有五條資料),但是employee表的rows怎麼是1,怎麼可能?剛開始也是很疑惑,這與mysql的查詢原理息息相關,rows實際反映的是查詢的內迴圈數,針對外層的每一條資料匹配,employee的確一槍就可以命中,因此rows為1。
通常情況下,如果將乙個主鍵放置到where後面作為條件查詢,mysql優化器就能把這次查詢優化轉化為乙個常量。至於如何轉化以及何時轉化,這個取決於優化器。
mysql 中explain的用法
最近在做效能測試中經常遇到一些資料庫的問題,通常使用慢查詢日誌可以找到執行效果比較差的sql,但是僅僅找到這些sql是不行的,我們需要協助開發人員分析問題所在,這就經常用到explain explain顯示了mysql如何使用索引來處理select語句以及連線表。可以幫助選擇更好的索引和寫出更優化的...
mysql中explain的用法
mysql中explain的用法 最近在做效能測試中經常遇到一些 資料庫的問題,通常使用慢查詢日誌可以找到執行效果比較差的sql,但是僅僅找到這些sql是不行的,我們需要協助開發人員分析問題所在,這就經常用到explain explain顯示了 mysql如何使用索引來處理select語句以及連線表...
mysql 中explain的用法
最近在做效能測試中經常遇到一些資料庫的問題,通常使用慢查詢日誌可以找到執行效果比較差的sql,但是僅僅找到這些sql是不行的,我們需要協助開發人員分析問題所在,這就經常用到explain explain顯示了mysql如何使用索引來處理select語句以及連線表。可以幫助選擇更好的索引和寫出更優化的...