explain 深入剖析 MySQL 索引及其效能優化指南
0.SQL標準的執行流程(select)
(8) SELECT (9) DISTINCT (11) <TOP_specification> <select_list> (1) FROM <left_table> (3) <join_type> JOIN <right_table> (2) ON <join_condition> (4) WHERE <where_condition> (5) GROUP BY <group_by_list> (6) WITH {CUBE ROLLUP} (7) HAVING <having_condition> (10) ORDER BY <order_by_list>
- FROM:對FROM子句中的前兩個表執行笛卡爾積,生成虛擬表VT1
- ON:對VT1應用ON篩選器。只有那些使<join_condition>為真的行才被插入VT2
- OUTER(JOIN):如果指定了OUTER JOIN,保留表中未找到匹配的行將作為外部行新增到VT2,生成VT3。如果FROM子句包含兩個以上的表,則對上一個聯接生成的結果表和下一個表重複執行步驟1到步驟3,直到處理完所有的表為止
- 對VT3應用WHERE篩選器。只有使<where_condition>為TRUE的行才被插入VT4
- GROUP BY:按GROUP BY 子句中的列列表對VT4中的行分組,生成VT5
- CUBEROLLUP:把超組插入VT5,生成VT6。
- HAVING:對VT6應用HAVING篩選器。只有使<having_condition>為TRUE的組才會被插入VT7
- SELECT:處理SELECT列表,產生VT8。
- DISTINCT:將重複的行從VT8中移除,產生VT9
- ORDER BY:將VT9中的行按ORDER BY子句中的列列表排序,生成一個有表(VC10)
- TOP:從VC10的開始處選擇指定數量或比例的行,生成表VT11,並返回給呼叫者
1.id:SQL執行的順利的標識。
首先 sql 從裡向外執行,而 id是一組數字,表示查詢中執行select子句或操作表的順序。
如果id相同,則執行順序從上至下。
如果是子查詢,id的序號會遞增,id越大則優先順序越高,越先會被執行。
id如果相同,則可以認為是一組,從上往下順序執行,所有組中,id越高,優先順序越高,越容易執行。
2.select_type:SELECT型別
1)簡單SELECT(不使用UNION或子查詢等)
2) PRIMARY:最外層的select
3)DERIVED:派生表的SELECT(FROM子句的子查詢)
4)UNION:UNION中的第二個或後面的SELECT語句
5)UNION RESULT:UNION的結果。
6)DEPENDENT UNION:UNION中的第二個或後面的SELECT語句,取決於外面的查詢
7)SUBQUERY:子查詢中的第一個SELECT
8)DEPENDENT SUBQUERY:子查詢中的第一個SELECT,取決於外面的查詢
PS:這裡我總結了下子查詢的in語句會用到DEPENDENT關鍵字,如果子查詢是union則是DEPENDENT UNION;如果子查詢是簡單的條件語句則是DEPENDENT SUBQUERY。這裡不一定準確是我自己總結的哈~~如果不對望指正
3.table:表的名字。
有時不是真實的表名字,看到的是derivedx(x是個數字,我的理解是第幾步執行的結果)
4.type:連線操作的型別。
MySQL執行計劃EXPLAIN主要可以通過type來進行分析:
type表示MySQL在表中找到所需行的方式,又稱“訪問型別”,常見型別如下:(從上至下,效果依次變好)
ALL:Full Table Scan。 index:Full Index Scan。
range:索引範圍掃描。
ref :非唯一性索引掃描。
eq_ref :唯一性索引掃描。
const,system:將查詢轉換為一個常量。
null:MySQL在優化過程中分解語句,執行時甚至不用訪問表或索引
ALL:ALL 表示”全表掃描”(full table scan), 效能是最差的幾種查詢之一,如果查詢的表比較大,且查詢頻次高,對MySQL資料庫有致命的效能影響。示例如下:
ename欄位上沒有索引,所以也是全表掃描。
index: index 表示“全索引掃描”(full index scan),其型別和ALL較類似,效能也是比較差; 和ALL區別在於只對索引樹進行掃描,但索引沒有起到過濾作用。
ID欄位為主鍵索引,在索引中掃描。
range:索引範圍掃描,對索引的掃描開始於某一點,返回匹配值域的行, 常見於between、<、>,IN等的查詢
ref:針對於非唯一或主鍵索引,或使用二者”最左部分欄位”索引的等值查詢或多表join,查詢效率由這個值返回的行數多少決定。
name和age和覆蓋索引,這裡只使用了name,即只使用了唯一性索引的一部分,故為ref。
eq_ref:eq_ref 使用於多表的join時,被驅動表的過濾欄位是主鍵或唯一索引,查詢效率很好。
MID對於表Manager是唯一的,主鍵索引,來與employee連線,故type為eq_ref。
const、system:const 針對主鍵或唯一索引的等值查詢掃描,最多隻返回一行資料。
system是const型別的特例,當查詢的表只有一行的情況下, 使用system。
exployee中ID為主鍵索引,可以直接定位,故為const,而衍生後的結果集A中只有一條記錄,故為type為system。
NULL:MySQL在優化過程中分解語句,執行時甚至不用訪問表或索引。
ID= SELECT MIN(ID)說明ID已確定,使用EXPLAIN EXTENDED優化,所以type為null,mysql自動優化,無需去訪問表或索引。
使用show warnings檢視優化後得到的結果。
index_merge:MySQL查詢優化器發現查詢可以同時使用多個索引查詢結果集進行並集或交集的情況,就會使用index_merge type。此時key欄位有兩個或多個索引, key_len/rows都分別有兩個數值; 如果是並集操作”Using intersect”, 往往通過兩個索引的欄位,合併為一個索引,避免index_merge查詢 下圖中兩個SQL一個是AND/OR, Using intersect 和Using union 分別表示使用兩個索引後的交集和並集
5.possible_keys:MySQL在搜尋資料記錄時可以選用的各個索引的名字。
表示MySQL查詢優化器發現當前查詢可能被使用地索引,但不一定能會利用,如果possible_key的列舉的索引越多,往往說明索引建立不合理,查詢效率不是最高效; 因為優化器會分析儘可能多的索引,評估哪個索引的“成本”消耗區域性最低,這個評估過程消耗時間和資源的。
6.key:它顯示了MySQL實際使用的索引的名字。
key資料列是MySQL實際選用的索引,如果它為空(或NULL),則MySQL不使用索引。
7.key_len:索引中被使用部分的長度,以位元組計。
key_len的值可以告訴你在聯合索引中mysql會真正使用了哪些索引。 在上例中,key_len是102,其中firstname佔50位元組,lastname佔50位元組,age佔2位元組(smallint儲存大小為2位元組)。如果MySQL只使用索引中的firstname部分,則key_len將是50。 在不損失精確性的情況下 ,key_len資料列裡的值越小越好(意思是更快)。
8.ref:顯示使用哪個列或常數與key一起從表中選擇行。
ref資料列給出了關聯關係中另一個數據表裡的資料列的名字。
9.rows:MySQL所認為的它在找到正確的結果之前必須掃描的記錄數。
MySQL查詢優化器根據統計資訊,估算SQL要查詢到結果集需要掃描讀取的資料行數; 這個值非常直觀顯示SQL的效率好壞,原則rows越少越好。顯然,這裡最理想的數字就是1。
10.extra:附加資訊
Using index和Using where會遇到的比較多,可以重點記下,其他的我沒怎麼遇到過了解即可,遇到具體問題可以查閱哈
1)Distinct
一旦MYSQL找到了與行相聯合匹配的行,就不再搜尋了
2)Not exists
MYSQL優化了LEFT JOIN,一旦它找到了匹配LEFT JOIN標準的行,就不再搜尋了
3)Range checked for each
沒有找到理想的索引,因此對於從前面表中來的每一個行組合,MYSQL檢查使用哪個索引,並用它來從表中返回行。這是使用索引的最慢的連線之一
4)Using filesort
MySQL需額外的排序操作,不能通過索引順序達到排序效果;又叫”檔案排序“,易錯誤理論為排序結果過大,記憶體中不夠需寫磁碟檔案排序。 一般有filesort,都建議優化去掉,CPU資源消耗大。 下圖last_update排序,但此欄位無索引,故需filesort
5)Using index
”覆蓋索引掃描“,表示查詢在索引樹中就可查詢所需資料,不用回表資料檔案(回表操作),往往說明效能不錯,這發生在對錶的全部的請求列都是同一個索引的部分的時候
6)Using temporary
看到這個的時候,查詢需要優化了。查詢有使用臨時表,一般出現於排序,分組和多表join的情況,查詢效率不高,建議優化。
7)Using where
使用了WHERE從句來限制哪些行將與下一張表匹配或者是返回給使用者。如果不想返回表中的全部行,並且連線型別ALL或index,這就會發生,或者是查詢有問題
先說到這,下面一篇給大家總結下如何選擇索引列以及使用索引的注意事項。
簡要解釋版本
EXPLAIN列的解釋:
列 |
描述 |
---|---|
table |
顯示這一行的資料是關於哪張表的。 |
type |
這是重要的列,顯示連線使用了何種型別。從最好到最差的連線型別為 const、eq_reg、ref、range、index和ALL。 |
possible_keys |
顯示可能應用在這張表中的索引。如果為空,沒有可能的索引。可以為相關的域從WHERE語句中選擇一個合適的語句。 |
key |
實際使用的索引。如果為NULL,則沒有使用索引。很少的情況下,MySQL會選擇優化不足的索引。這種情況下,可以在SELECT語句中使用USE INDEX(indexname) 來強制使用一個索引或者用IGNORE INDEX(indexname)來強制MySQL忽略索引。 |
key_len |
使用的索引的長度。在不損失精確性的情況下,長度越短越好。 |
ref |
顯示索引的哪一列被使用了,如果可能的話,是一個常數。 |
rows |
MySQL認為必須檢查的用來返回請求資料的行數。 |
Extra |
關於MySQL如何解析查詢的額外資訊。將在表4.3中討論,但這裡可以看到的壞的例子是Using temporary和Using filesort,意思MySQL根本不能使用索引,結果是檢索會很慢。 |
extra列返回的描述的意義:
值 |
意義 |
---|---|
Distinct |
一旦MySQL找到了與行相聯合匹配的行,就不再搜尋了。 |
Not exists |
MySQL優化了LEFT JOIN,一旦它找到了匹配LEFT JOIN標準的行,就不再搜尋了。 |
Range checked for each Record(index map:#) |
沒有找到理想的索引,因此對於從前面表中來的每一個行組合,MySQL檢查使用哪個索引,並用它來從表中返回行。這是使用索引的最慢的連線之一。 |
Using filesort |
看到這個的時候,查詢就需要優化了。MySQL需要進行額外的步驟來發現如何對返回的行排序。它根據連線型別以及儲存排序鍵值和匹配條件的全部行的行指標來排序全部行。 |
Using index |
列資料是從僅僅使用了索引中的資訊而沒有讀取實際的行動的表返回的,這發生在對錶的全部的請求列都是同一個索引的部分的時候。 |
Using temporary |
看到這個的時候,查詢需要優化了。這裡,MySQL需要建立一個臨時表來儲存結果,這通常發生在對不同的列集進行ORDER BY上,而不是GROUP BY上。 |
Where used |
使用了WHERE從句來限制哪些行將與下一張表匹配或者是返回給使用者。如果不想返回表中的全部行,並且連線型別ALL或index,這就會發生,或者是查詢有問題不同連線型別的解釋(按照效率高低的順序排序)。 |
system |
表只有一行 system 表。這是const連線型別的特殊情況 。 |
const |
表示通過主鍵或惟一索引一次就找到了,查詢時間為 O(1),可以認為是個常數(constant),所以叫 const; |
eq_ref |
在 join 的時候,對於每個索引鍵,表中只有唯一一條記錄與之匹配,簡單來說就是多表連線中使用primary key或者 unique key作為關聯條件; |
ref |
與 eq_ref 區別是用了非唯一索引掃描; |
range |
這個連線型別使用索引返回一個範圍中的行,比如使用>或<查詢東西時發生的情況。 |
index |
這個連線型別對前面的表中的每一個記錄聯合進行完全掃描(比ALL更好,因為索引一般小於表資料)。 |
ALL |
這個連線型別對於前面的每一個記錄聯合進行完全掃描,這一般比較糟糕,應該儘量避免。 |
REF:
[1] 細說MySQL Explain和Optimizer Trace簡介
https://zhuoroger.github.io/2016/08/11/mysql-explain/
[2] 如何理解 MySQL 的執行計劃
http://blog.decaywood.me/2017/04/07/the-execution-plan-of-MySQL/