1. 程式人生 > 資料庫 >MySQL效能優化(七):MySQL執行計劃,真的很重要

MySQL效能優化(七):MySQL執行計劃,真的很重要

一條SQL被一個懵懂的少年,一陣蹂躪,扔向了MySQL伺服器的盡頭,少年苦苦等待,卻遲遲等不來那滿載而歸的碩果。於是少年氣憤,費盡苦心想從度娘那邊尋求幫助,面對執行計劃EXPLAIN,卻等來的是無盡的折磨與抓狂。

通過explain命令,根據執行計劃找到存在效能問題的SQL語句,以幫助我們優化SQL提供方向和依據。

如果面對執行計劃,你也是一臉疑惑,甚至抓狂,那麼你真的需要認真的來了解它了。在資料庫效能優化中,執行計劃,真的很重要,通過執行計劃能夠幫助我們更加明確的來進行SQL優化。本文將從執行計劃開始說起,講解執行計劃該如何用,其中各個列的含義究竟是什麼。

一、執行計劃?

執行計劃,就是一條SQL語句,在資料庫中實際執行的時候,一步步的分別都做了什麼。也就是我們用EXPLAIN

分析一條SQL語句時展示出來的那些資訊。

EXPLAIN命令是檢視查詢優化器是如何決定執行查詢的主要方法,從它的查詢結果中可以知道一個SQL語句每一步是如何執行的,都經歷了些什麼,分為哪幾步,有沒有用到索引,哪些欄位用到了什麼樣的索引,是否有一些可優化的地方等,這些資訊都是我們SQL優化的依據。

要使用·EXPLAIN,只需在查詢中的SELECT關鍵字之前增加EXPLAIN。語法如下:

EXPLAIN + SELECT查詢語句;

當執行執行計劃時,只會返回執行計劃中每一步的資訊,它會返回一行或多行資訊,顯示出執行計劃中的每一部分和執行的次序。

如:
在這裡插入圖片描述
如果查詢的是多個關聯表,執行計劃結果可能是多行。

在接下來涉及到的示例表,均來自於MySQL官方的示例資料庫sakila

,指令碼下載:https://downloads.mysql.com/docs/sakila-db.zip

二、執行計劃中的列

EXPLAIN的結果總是有相同的列,每一列代表著不同的含義,可變的只是行數和內容。從上面的例子中,我們看到返回的有很多列,為了更加清楚的瞭解每一列的含義,便於我們更好的完成優化SQL。

涉及到的列有:

列名含義
idid列,表示查詢中執行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
SUBQUERYselectwhere列表中包含了子查詢
DERIVED表示包含在from子句的子查詢中的select,MySQL會遞迴執行並將結果放到一個臨時表中,稱其為“派生表”,因為該臨時表是從子查詢中派生而來的。
UNION第二個select出現在UNION之後,則被標記為UNION
UNION RESULTUNION表獲取結果的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類似,掃描表時按索引次序進行,而不是按行掃描,即:只遍歷索引樹。

indexALL雖然都是讀全表,但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 bygroup by

4)Using filesort

對資料使用了一個外部的索引排序,而不是按照表內的索引進行排序讀取。也就是說MySQL無法利用索引完成的排序操作成為“檔案排序”。

三、總結

通過上述對執行計劃的瞭解,我們能夠從中得到什麼?

  • SQL如何使用索引

  • 複雜SQL的執行順序

  • 查詢掃描的資料函式

  • ……

當面臨不夠優的SQL時,我們首先要檢視其執行計劃,根據執行計劃結果來分析可能存在哪些問題,從而幫助、指導我們是否新增索引、是否調整SQL順序、是否避免不應該的書寫方式等等。

以上就是這篇文章的全部內容,希望本文的內容對大家在SQL效能優化、SQL書寫時,有一定的幫助。

執行計劃,真的很重要,尤其是SQL調優時,很香!

MySQL效能優化(一):MySQL架構與核心問題

MySQL效能優化(二):選擇優化的資料型別

MySQL效能優化(三):深入理解索引的這點事

MySQL效能優化(四):如何高效正確的使用索引

MySQL效能優化(五):為什麼查詢速度這麼慢

MySQL效能優化(六):常見優化SQL的技巧

MySQL效能優化(七):MySQL執行計劃,真的很重要