您好,登錄后才能下訂單哦!
小編給大家分享一下MySQL 5.7Explain執行計劃,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
目錄
1. 介紹
2. Explain 結果列詳解
2.1 id
2.2 select_type
2.3 table
2.4 partitions
2.5 type(非常重要)
2.6 possible_keys
2.7 key
2.8 key_len
2.9 ref
3.10 rows
2.11 filtered
2.12 Extra
【注】
當前系統環境: MySQL 5.7
,其他版本略有不同,后期會抽時間單獨說明。
只介紹常見的場景,其他少見的場景暫不研究,如有需要可以去官方文檔中查找。
非入門,需要對 MySQL
的底層數據結構 B+
樹有一定的了解。
文檔參考:
MySQL 官方 Explain 文檔
1. 介紹
使用 EXPLAIN
關鍵字可以模擬優化器執行 SQL
語句,并分析查詢語句的性能瓶頸。
2. Explain 結果列詳解
2.1 id
id
列的編號是 select
的序列號,一般有幾個 select
就有幾個 id
(聯表查詢會有重復的 id
),并且 id
的順序是按 select
出現的順序增長的。
id
越大則表示執行的優先級越高,id
相同(一般出現在聯表查詢)則從上往下執行,id
為 NULL
最后執行。
2.2 select_type
select_type
表示對應行是簡單的還是復雜的查詢。常見的值有:
simple
:簡單查詢,查詢不包含子查詢和union。
primary
:復雜查詢中最外層的 select 。
subquery
:包含在 select 中的子查詢(不在 from 子句中)
derived
:包含在 form 子句中的子查詢,MySQL 會將結果放在一個臨時表中,也稱為派生表。
union
:在 union 中的第二個或之后的 select。
【注】在 MySQL 5.7
中,會對衍生表進行合并優化,如果要直觀的查看 select_type
的值,需要臨時關閉該功能(默認是打開的),下面的介紹中凡是涉及到衍生表的都需要該操作。
# 關閉衍生表的合并優化(只對該會話有效)set session optimizer_switch='derived_merge=off'; # 打開衍生表的合并優化(只對該會話有效)set session optimizer_switch='derived_merge=on';
2.3 table
對應行查詢的表。
【注】
當 from 子句
中有子查詢時,table 列為是 <derivenN>
的格式,表示這一行的執行的是 id = N
行的查詢。
當有 union
時,table 的數據為 <union M,N>
的格式,M 和 N
表示參與 union
的 select
行id
。
2.4 partitions
未完待續。。。
2.5 type(非常重要)
type
表示這行查詢的關聯類型(訪問類型,或查詢類型),通過該值可以了解該行查詢數據記錄的大概范圍。
常見的值依次從最優到最差分別為:system > const > eq_ref > ref > range > index > ALL
;一般我們要保證效率的話,要優化我們的語句至少使其達到 range
級別,如果可能的話做好優化到 ref
;range
一般用于范圍查找,所以換句話說除了范圍查找,其他的查詢語句我們最好是優化到 ref
級別。
常見值說明:
NULL
: 表示 MySQL
能夠在優化階段分解查詢語句,在執行階段不用訪問表和索引。
system / const
: MySQL 能對某個查詢部分進行優化并將其轉化成一個常量(可以通過 show warnings
查看優化的結果),主要是查詢主鍵(Primary Key
)或唯一鍵索引(Unique Key
)對應的記錄,因為不存在重復,所以最多只能查詢出一條記錄,所以速度比較快。system
是 const
的特例,當臨時表里只有一條記錄時為 system
。
# 表里有一個主鍵id為1的記錄 - constexplain select * from student where id = 1# 派生表里面只有一條記錄 - systemexplain select * from (select * from student where id = 1) tmp# 注: 如果查詢的列中有 text 類型,那么在這里 type 會變為 ALL ,# 因為無法使用內存臨時表,只能在磁盤上創建臨時表,所以性能上會有所損耗,效果等同于全表查詢 ALL。
req_ref
:當主鍵或唯一鍵索引的相關列并聯接使用時(聯表查詢),最多匹配一條符合條件的記錄。這是除了 const
之外的最好的聯接類型,簡單的 select
查詢不會出現 req_ref
,更多出現在聯表查詢。
# 雖然返回結果中有多條記錄,但是在查詢中一個學生id只對應一個班級,所以查詢班級的時候為 req_ref,# 但是查詢 student 的時候是 ALL,全表查詢explain select * from student left join banji on student.id = banji.student_id
【注】在查詢的過程中的返回結果如下:
當聯接表查詢時候會看作是一條查詢 SQL
,所以它們對應的 id
是一樣的,當 id
都是一樣的時候,按照從上到下
的順序依次執行,這里是先查詢班級所有的學生(全表查詢 ALL
),然后根據學生id
查找出學生對應的班級信息(req_ref
)。
ref
:當使用普通索引(Normal)
或者是聯合索引的部分前綴
時,索引要和某個值進行比較,可能會找到多個符合條件的記錄行,從輔助索引的根節點開始對比并找到相應的記錄。
# 簡單的 select 查詢,name 是普通索引(Normal Index)explain select * from student where name = '張三';# 簡單 select 查詢,banji_id (第一個) 和 student_id (第二個) 的聯合索引EXPLAIN SELECT * FROM banji_student WHERE banji_student.banji_id = 3# 關聯表查詢# 包含 banji 表,banji_student 是班級與學生的關系表# 關系表中有 banji_id (第一個) 和 student_id (第二個) 的聯合索引 idx_banji_stu_id 索引,# 以下查詢只用到了聯合索引的 banji_id (第一個)explain select * from banji_id from banji left join banji_student on banji.id = banji_student.banji_id
range
:范圍掃描,通常出現在 in,between,>,<,>=
等操作中,使用一個索引來檢索給定范圍的行。
# 查詢 id 大于 1 的學生信息explain select * from student where id > 2;
index
:
# student 表只有id主鍵,name 普通索引select * from student;# 這個時候會走 name 索引# 因為 name 是普通索引,所以如果加 where 的話可以達到 ref 級別select * from student where name = 'Ana'
覆蓋索引
定義:覆蓋索引一般針對于輔助索引,并不是真正的索引,只是索引查找的一種方式。如果 select
查詢的字段都在輔助索引樹中全部拿到,這種情況一般是使用了覆蓋索引
,不需要通過輔助索引樹
找到主鍵
,再通過主鍵
去主鍵索引樹
里獲取其它字段值。
掃描全索引就能拿到結果,一般是掃描某個二級索引
(輔助索引,除了主鍵之外的索引
)。這種索引不會從主鍵索引樹根節點開始查找,而是直接對二級索引的葉子節點遍歷和掃描,從而查找出相應的記錄行,速度比較慢;
這種查詢方式一般為使用覆蓋索引
,查詢所需的所有結果集在二級索引
與主鍵索引
中都有的情況下,由于二級索引
一般比較小(因為二級索引
是非聚集
的,其葉子節點是存放的主鍵索引
相應的地址,而主鍵索引
是聚集的,其葉子節點存放的是完整的數據集),所以優先走二級索引,這種情況通常比 ALL
快一些。
在某些情況下,如果表的列數特別多,這個時候通過輔助索引
查詢的性能就不如直接使用主鍵索引
效率高(如果查詢了輔助索引
的話,還會返回到主鍵索引中進行查找更多的字段,也就是回表查詢
,當然在某些情況下使用回表查詢
的性能也會比只使用主鍵索引
的性能高),這個時候會走主鍵索引,這種情況也比 ALL
快。
ALL
:全表掃描,掃描主鍵(聚簇、聚集)索引樹的所有葉子節點,通常這種情況下要根據業務場景來增加其他索引進行優化。
# id 為主鍵的 student 表,沒有其他索引,該查詢為 ALL.select * from student
2.6 possible_keys
possible_keys
主要顯示查詢可能用到哪些索引來查找,只是可能會使用,并不代表一定會使用。
常見值說明:
NULL
: 沒有相關索引,如果是 NULL
的話,可以考慮在 where 子句
中創建一個適當的索引來提高查詢性能,然后繼續用 explain
查看其效果;也有可能出現 possible_keys
為 NULL
,但是 key
有值,實際走了索引。
有列值:如果顯示表中的某列,則表示可能會走這一列對應列值的索引;如果 possible_keys
有值,但是 key
顯示 NULL
,這種情況一般存在于表中數據量不大的情況,因為 MySQL
語句優化器認為索引對此查詢的幫助不大,從而選擇了全表查詢
。
2.7 key
key
表示 MySQL
實際采用哪個索引來優化對該表的查詢。
如果沒有使用索引,則該列為 NULL
,如果想強制 MySQL
使用或忽略 possible_keys
列中的索引,可以在查詢中使用 force index
或 ignore index
.
2.8 key_len
顯示了 MySQL
索引所使用的字節數
,通過這個數值可以計算具體使用了索引中的哪些列(主要用于聯合索引的優化)。
【注】索引最大長度是 768 字節
,當字符串過長時,MySQL
會做一個類似左前綴索引的處理,將前半部分的字符提取出來做索引。
示例:一個學生與班級的關系表:
banji_student
,存在使用banji_id
與student_id
兩個列組合的聯合索引,并且每個索引int
都是4
字節,通過key_len
值為4
可以知道只使用了聯合索引的第一列:banji_id
來執行索引查找。
# 只使用了聯合索引的第一列select * from banji_student where banji_id = 2
key_len
的計算規則如下:
字符串:常見的是 char(n)
和 varchar(n)
,從 MySQL 5.0.3
之后,n
均表示字符數
,而不是字節數
,如果是 UTF-8
,一個數字或字母占1
個字節,一個漢字占3
個字節。
描述 | |
---|---|
char(n) | 非漢字長度為 n ,如果存放漢字長度為 3n 字節 |
varchar(n) | 非漢字長度為 n+2 ,如果存放漢字長度為 3n+2 字節;因為 varchar 是可變長字符串,需要 2 字節來存儲字符串長度 |
數值類型:
描述 | |
---|---|
tinyint | 長度為 1 字節 |
smallint | 長度為 2 字節 |
int | 長度為 4 字節 |
bigint | 長度為 8 字節 |
時間類型:
描述 | |
---|---|
date | 長度為 3 字節 |
timestamp | 長度為 4 字節 |
datetime | 長度為 8 字節 |
NULL
如果字段允許設置為 NULL
,則需要 1
字節來記錄是否為 NULL
; Not NULL
的列則不需要。
2.9 ref
顯示了在使用 key
列中實際的索引時,表查找時所用到的列名和常量;常見的為 const
常量或索引關聯查詢的字段(列)名
。
# 使用了常量 2,所以在查詢的時候 ref 為 constselect * from student where id = 2# 關聯表查詢# 包含 banji 表,banji_student 是班級與學生的關系表# 關系表中有 banji_id (第一個) 和 student_id (第二個) 的聯合索引 idx_banji_stu_id 索引# 這里的 ref 為 test.id ,也就是指的是 banji.idexplain select * from banji_id from banji left join banji_student on banji.id = banji_student.banji_id
3.10 rows
顯示預計查詢的結果數,并不是真正的結果集中的記錄(行)數,僅供參考。
2.11 filtered
未完待續。。。
2.12 Extra
這一列展示的是額外的信息,存在很多值,且在不同的場景下以及不同版本的 MySQL
所表示的意思也不同,只能是表示大概的意思并且僅做優化參考,這里只介紹常見的值。
Using index
:使用覆蓋索引,在 type
相同的情況下, Extra
的值為 Using index
要比為 NULL
性能高。
比如 banji
表,存在 id,name,create_time
列,存在 id 主鍵
與 name 普通索引
。
# 覆蓋索引,直接查詢 name 對應的索引樹就可以滿足 select 后面的查詢列select id,name from banji# 非覆蓋索引,雖然也走了索引,但是進行了回表查詢,以查詢出 create_time 字段。select * from banji where name = '二年級'
Using where
:使用 where
關鍵字來查詢,并且對應的列沒有設置索引,對應的 key
為 NULL
。
這種情況一般要對查詢的列添加相對應的索引來進行優化。
Using index condition
:非覆蓋索引查詢并進行了回表,并且輔助索引使用了條件查詢語句(where
或其他)。
比如 banji_student
關系表,存在 id,banji_id,student_id,create_time
列,存在 id 主鍵
和 banji_id 與 student_id 的組合(聯合)索引
。
# 進行了回表查詢,以查詢出 create_time 列,并且組合索引進行了范圍查找select * from banji_student where banji_id > 3
Using temporary
:MySQL
需要創建創建一個臨時表來處理查詢,出現這種情況一般要添加索引進行優化處理。
# 如果 name 沒有添加普通索引的話,則需要創建一個臨時表來進行去重,Extra 值為 Using temporary# 如果添加了索引,則會走 name 對應的索引樹,并且是覆蓋索引,Extra 值為 Using indexexplain select distinct name from student
Using filesort
:使用外部排序而不是索引排序,當數據較小的時候采用的是內存排序,當數據量較大的時候會頻繁的訪問磁盤,并將排序后的數據寫入磁盤。
# 如果 name 沒有添加普通索引的話,則需要創建一個臨時表來進行去重,Extra 值為 Using filesort# 如果添加了索引,則會走 name 對應的索引樹,并且是覆蓋索引,Extra 值為 Using indexexplain select name from student order by name
Select tables optimized away
:使用聚合函數
(例如 max
、min
等)來訪問存在索引的字段時,只訪問索引樹中已排好序的葉子,節點性能很高。
# 比如使用聚合函數 min 查詢最小的學生 id(主鍵)explain select min(id) from student
以上是“MySQL 5.7Explain執行計劃”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。