MySQL效能優化(七):MySQL執行計劃,真的很重要
一條SQL被一個懵懂的少年,一陣蹂躪,扔向了MySQL伺服器的盡頭,少年苦苦等待,卻遲遲等不來那滿載而歸的碩果。於是少年氣憤,費盡苦心想從度娘那邊尋求幫助,面對執行計劃EXPLAIN
,卻等來的是無盡的折磨與抓狂。
通過explain
命令,根據執行計劃找到存在效能問題的SQL語句,以幫助我們優化SQL提供方向和依據。
如果面對執行計劃,你也是一臉疑惑,甚至抓狂,那麼你真的需要認真的來了解它了。在資料庫效能優化中,執行計劃,真的很重要,通過執行計劃能夠幫助我們更加明確的來進行SQL優化。本文將從執行計劃開始說起,講解執行計劃該如何用,其中各個列的含義究竟是什麼。
一、執行計劃?
執行計劃,就是一條SQL語句,在資料庫中實際執行的時候,一步步的分別都做了什麼。也就是我們用EXPLAIN
EXPLAIN
命令是檢視查詢優化器是如何決定執行查詢的主要方法,從它的查詢結果中可以知道一個SQL語句每一步是如何執行的,都經歷了些什麼,分為哪幾步,有沒有用到索引,哪些欄位用到了什麼樣的索引,是否有一些可優化的地方等,這些資訊都是我們SQL優化的依據。
要使用·EXPLAIN
,只需在查詢中的SELECT
關鍵字之前增加EXPLAIN
。語法如下:
EXPLAIN + SELECT查詢語句;
當執行執行計劃時,只會返回執行計劃中每一步的資訊,它會返回一行或多行資訊,顯示出執行計劃中的每一部分和執行的次序。
如:
如果查詢的是多個關聯表,執行計劃結果可能是多行。
在接下來涉及到的示例表,均來自於MySQL官方的示例資料庫
sakila
,指令碼下載:https://downloads.mysql.com/docs/sakila-db.zip
二、執行計劃中的列
EXPLAIN
的結果總是有相同的列,每一列代表著不同的含義,可變的只是行數和內容。從上面的例子中,我們看到返回的有很多列,為了更加清楚的瞭解每一列的含義,便於我們更好的完成優化SQL。
涉及到的列有:
列名 | 含義 |
---|---|
id | id列,表示查詢中執行select子句或操作表的順序。 |
select_type | 查詢型別,主要是用於區分普通查詢、聯合查詢、子查詢等複雜的查詢。 |
table | 表明對應行正在訪問的是哪個表。 |
partitions | 查詢涉及到的分割槽。 |
type | 訪問型別,決定如何查詢表中的行。 |
possible_keys | 查詢可以使用哪些索引。 |
key | 實際使用的索引,如果為NULL,則沒有使用索引。 |
key_len | 索引中使用的位元組數,查詢中使用的索引的長度(最大可能長度),並非實際使用長度,理論上長度越短越好。 |
ref | 顯示索引的那一列被使用。 |
rows | 估算出找到所需行而要讀取的行數。 |
filtered | 返回結果的行數佔讀取行數的百分比,值越大越好。 |
Extra | 額外資訊,但又十分重要。 |
1. id列
id列是一個編號,用於標識SELECT
查詢的序列號,表示執行SQL查詢過程中SELECT
子句或操作表的順序。
如果在SQL中沒有子查詢或關聯查詢,那麼id列都將顯示一個1。否則,內層的SELECT
語句一般會順序編號。
id列分為三種情況:
1)id相同
如下普通查詢,沒有子查詢。
explain select f.* from film f,film_actor fa,actor a where f.film_id = fa.film_id and fa.actor_id = a.actor_id and a.first_name = 'NICK';
2)id不同
如果存在子查詢,id的序號會遞增,id值越大優先順序越高,越先被執行。
explain select * from film where film_id = (select film_id from film_actor where actor_id = 2 limit 1);
3)id相同又不同
1)、2)兩種情況同時存在。id如果相同,認為是一組,從從上往下執行。在所有組中,id值越大,優先順序越高,越先執行。
2. select_type列
select_type
列表示對應行的查詢型別,是簡單查詢還是複雜查詢,主要用於區分普通查詢、聯合查詢、子查詢等複雜的查詢。
select_type
列有如下值:
select_type值 | 說明 |
---|---|
SIMPLE | 簡單查詢,意味著不包括子查詢或UNION 。 |
PRIMARY | 查詢中包含任何複雜的子部分,最外層查詢則被標記為PRIMARY |
SUBQUERY | 在select 或where 列表中包含了子查詢 |
DERIVED | 表示包含在from 子句的子查詢中的select ,MySQL會遞迴執行並將結果放到一個臨時表中,稱其為“派生表”,因為該臨時表是從子查詢中派生而來的。 |
UNION | 第二個select出現在UNION 之後,則被標記為UNION 。 |
UNION RESULT | 從UNION 表獲取結果的select 。 |
3. table列
table
列表示對應行正在執行的哪張表,指代對應表名,或者該表的別名(如果SQL中定義了別名)。
4. partitions列
查詢涉及到的分割槽。
5. type列
type
列指代訪問型別,是MySQL決定如何查詢表中的行。
是SQL查詢優化中一個很重要的指標,擁有很多值,依次從最差到最優:
ALL < index < range < index_subquery < unique_subquery < index_merge < ref_or_null < fulltext < ref < eq_ref < const < system
1)ALL
眾所周知的全表掃描,表示通過掃描整張表來找到匹配的行,很顯然這樣的方式查詢速度很慢。
這種情況,效能最差,在寫SQL時儘量避免此種情況的出現。
舉例如下:
explain select * from film;
在平時寫SQL時,避免使用select *
,就不難理解了。換言之,是為了避免全表掃描,因為全面掃描是效能最差的。
2)index
全索引掃描,和全表掃描ALL
類似,掃描表時按索引次序進行,而不是按行掃描,即:只遍歷索引樹。
index
與ALL
雖然都是讀全表,但index
是從索引中讀取,而ALL是從硬碟讀取。顯然,index
效能上優於ALL
,合理的新增索引將有助於效能的提升。
舉例如下:
explain select title from film; explain select description from film;
通過explain結果來看,只查詢表film
中欄位title
時,是按照索引掃描的(type
列為index
),倘若查詢欄位description
,卻是按照全表掃描的(type
列為ALL
)。這是為何呢?
接下來,我們不妨看看錶film的結構:
從desc film
結果來看,欄位title
建立的有索引,而欄位description
沒有,所以select title from film
是按索引掃描,而select description from film
按全表掃描。
從上面的舉例對比中,也充分印證了索引的重要性。
3)range
只檢索給定範圍的行,使用一個索引來選擇行。key
列顯示使用了那個索引。一般就是在where
語句中出現了bettween、<、>、in
等的查詢。這種索引列上的範圍掃描比全索引掃描index
要好。
舉例如下:
explain select * from film where film_id between 1 and 10;
4)ref
非唯一性索引掃描,返回匹配某個單獨值的所有行。本質是也是一種索引訪問,它返回所有匹配某個單獨值的行,然而它可能會找到多個符合條件的行,所以它屬於查詢和掃描的混合體。
此型別只有當使用非唯一索引或者唯一索引的非唯一性字首時,才會發生。
舉例如下:
show index from film; explain select * from film where title = 'ACADEMY DINOSAUR';
5)eq_ref
**唯一索引掃描。**常見於主鍵或唯一索引掃描。
6)const
通過索引一次就能找到,const
用於比較primary key
或者unique
索引。因為只需匹配一行資料,所有很快。如果將主鍵置於where
列表中,mysql就能將該查詢轉換為一個const
。
舉例如下:
show index from film; explain select * from film where film_id = 1;
7)system
表只有一行記錄,這是const型別的特例,比較少見,如:系統表。
6. possible_keys列
顯示在查詢中使用了哪些索引。
7. key列
實際使用的索引,如果為NULL
,則沒有使用索引。查詢中如果使用了覆蓋索引,則該索引僅出現在key列中。
possible_keys
列表明哪一個索引有助於更高效的查詢,而key
列表明實際優化採用了哪一個索引可以更加高效。
舉例如下:
show index from film_actor; explain select actor_id,film_id from film_actor;
8. key_len列
表示索引中使用的位元組數,查詢中使用的索的長度(最大可能長度),並非實際使用長度,理論上長度越短越好。key_len
是根據表定義計算而得的,不是通過表內檢索出的。
9. ref列
表示在key
列記錄的索引中查詢值,所用的列或常量const
。
10. rows列
估算出找到所需行而要讀取的行數。
這個數字是內嵌迴圈關聯計劃裡的迴圈數,它並不是最終從表中讀取出來的行數,而是MySQL為了找到符合查詢的那些行而必須讀取行的平均數,只能作為一個相對數來進行衡量。
11. filtered列
返回結果的行數佔讀取行數的百分比,值越大越好。
舉例如下:
表film_actor中actor_id為1的記錄有19條,而SQL查詢時掃描了19行(rows:19),19條符合條件(filtered: 100 19/19)
12. Extra列
額外資訊,但又十分重要。
常見的值如下:
1)Using index
表示SQL中使用了覆蓋索引。
舉例如下:
2)Using where
許多where
條件裡是涉及索引中的列,當它讀取索引時,就能被儲存引擎檢驗,因此不是所有帶·where
子句的查詢都會顯示“Using where”
。
3)Using temporary
對查詢結果排序時,使用了一個臨時表,常見於order by
和group by
。
4)Using filesort
對資料使用了一個外部的索引排序,而不是按照表內的索引進行排序讀取。也就是說MySQL無法利用索引完成的排序操作成為“檔案排序”。
三、總結
通過上述對執行計劃的瞭解,我們能夠從中得到什麼?
SQL如何使用索引
複雜SQL的執行順序
查詢掃描的資料函式
……
當面臨不夠優的SQL時,我們首先要檢視其執行計劃,根據執行計劃結果來分析可能存在哪些問題,從而幫助、指導我們是否新增索引、是否調整SQL順序、是否避免不應該的書寫方式等等。
以上就是這篇文章的全部內容,希望本文的內容對大家在SQL效能優化、SQL書寫時,有一定的幫助。
執行計劃,真的很重要,尤其是SQL調優時,很香!