聲明:如下均來自於MySQL英文手冊5.6,本文主要針對執行計劃,因爲本人才疏學淺,其餘部分可能會一句話帶過。算法
引言.什麼是執行計劃:緩存
The set of operations that the optimizer chooses to perform the most efficient query is called the 「query execution plan」, also known as the EXPLAIN plan。oop
優化器爲了最有效的執行查詢而選擇的一系列操做被稱爲執行計劃。性能
1.MySQL全部的join都是使用 nest-loop join 算法(嵌套循環算法),固然了這裏包括了它的一些變種算法。這裏順便提下MySQL的一些join算法:優化
塊嵌套循環 (block nest-loop join)主要思想就是將表分爲多個塊來執行;
spa
BKA算法,就是Batch Key Access(5.6.3纔有的實現) ,適用於使用索引的時候,利用一種叫MRR的接口(multi-range read)將針對join buffer裏的行,構建出須要的鍵,並批量提交給MRR引擎,而後MRR引擎會以一種較優的方式(引擎相關的)在索引中查找,這種方式其實就是:根據鍵獲取rowID或者主鍵放在緩衝區,而後排序,再按順序去獲取那些行,這就使得本來的隨機IO變成了磁盤的順序IO了,速度將會比原來的快,最後返回匹配的行。須要注意的一點是,若是自己該查詢是索引覆蓋的,那就根本不須要訪問表,直接訪問索引樹就好了,所以就不會使用BKA算法了;
orm
至於MRR,除了上面的好處外,它自己也會經過將二級索引的查找條件合併,減小無關索引的讀取,來提升速度。總之,MRR的好處就是批處理+排序。
排序
2.對於一組joins,MySQL的join算法會從第一個表讀取一行,而後一直日後逐個表找匹配行,若是某一行可以從第一個表開始,以後每一個表都能找到匹配的行,則輸出該「大行」;而後原路返回到以前的表直到能找到一張包含匹配行的表爲止,而後繼續向後面的每一個表找匹配的行。索引
3.MySQL中索引的使用會被索引的cardinality所影響,cardinality就是基數,集合的勢的意思,過低會致使索引被棄用,舉個例子,若是向sex這種取值太單一的字段創建索引,該索引可能不會被使用,由於基數過小了。能夠經過Analyze Table tbl_name,來分析表,更新表的統計數據(InnoDB,MyISAM通常會自動更新)。接口
4.執行計劃輸出(Explain Output)中各列的解釋:
列名 |
解釋 |
特殊說明 |
id | select的標識符 | select在查詢中的序號,同序號就代表是一組,序號的組越大越先執行,越外層數值越小,若是是union結果則是NULL,同組的話按照從上到下的順序執行。 |
select_type | select類型 | 沒有子查詢或union時都是simple,不然會有primary和union之類的,這裏要注意帶有uncacheable的類型,表示沒法緩存,外層行切換會致使從新計算該select |
table | 輸出行的所屬表 | 表名或<unionM,N>,<derivedN>,<subqueryN> |
partitions | 匹配的分區 | 涉及到表的分區,沒有使用分區則是NULL |
type | join類型 | 第5點有詳細說明 |
possible_keys | 可能被選擇的索引 | 用於查找行的索引,獨立於執行順序的,這意味着不必定會使用,只是可能 |
key | 實際被選擇的索引 | 可能會出現不在possible_keys的的key的狀況。就是在沒法使用possible_keys的時候,若是其他的某個索引覆蓋了被選擇的列,即該索引不能用於決定是否獲取行,但因爲索引掃描更高效,所以MySQL也會使用該索引來加速 |
key_len | 被選擇的鍵的長度 | MySQL在多部分索引中使用的部分的長度,可能有多個值 |
ref | 須要與索引比較的列 | 列名或者const(常數,where id = 1的時候就是const了) |
rows | 估計要被檢驗的行數 | InnoDB中不必定精確,只是一個估計值 |
filtered | 被表的條件所過濾的行的百分比 | 估計值 |
extra | 額外信息 | 內容太多,須要再查文檔吧 |
5.type(join的類型):
類型 |
說明 |
system | 表只有一行 |
const | 表最多隻有一行匹配 |
eq_ref | 每次與以前的表合併行都只在該表讀取一行,這是除了system,const以外最好的一種,特色是使用=,並且索引的全部部分都參與join且索引是主鍵或非空惟一鍵的索引 |
ref | 若是每次只匹配少數行,那就是比較好的一種,使用=或<=>,能夠是左覆蓋索引或非主鍵或非惟一鍵 |
fulltext | 全文搜索 |
ref_or_null | 與ref相似,但包括NULL |
index_merge | 表示出現了索引合併優化(包括交集,並集以及交集之間的並集),但不包括跨表和全文索引。這個比較複雜,目前的理解是合併單表的範圍索引掃描(若是成本估算比普通的range要更優的話) |
unique_subquery | 在in子查詢中,就是value in (select...)把形如「select unique_key_column」的子查詢替換。PS:因此不必定in子句中使用子查詢就是低效的! |
index_subquery | 同上,但把形如」select non_unique_key_column「的子查詢替換 |
range | 常數值的範圍 |
index | 1.當查詢是索引覆蓋的,即全部數據都可從索引樹獲取的時候(Extra中有Using Index);2.以索引順序從索引中查找數據行的全表掃描(無 Using Index)。另外Extra中Using Index與Using Where同時出現的話,則是利用索引查找鍵值的意思;如單獨出現,則是用讀索引來代替讀行,但不用於查找的,也就是4中提到key的特例 |
all | 全表掃描 |
6.評估查詢性能能夠經過計算磁盤尋址次數:
小表通常一次尋址能夠讀取一行,由於索引可能被緩存。
大表則能夠經過下列公式:log(row_count) /log(index_block_length / 3 * 2 / (index_length + data_pointer_length)) +1。
若是是寫入的話,一般須要四次尋址,其中一次尋址是尋找插入新索引的地方,一般更新索引須要兩次(B樹插入新節點後調整的平均次數是兩次?Why?統計學原理?),最後一次寫入該行。
7.select @@optimizer_switch;或show variables like 'optimizer_switch';能夠查看優化器的一些選項。