MySQL怎么打开explain_MySQL干货之-利用EXPLAIN优化查询
? 在工作中,經常會碰到一些慢查詢,Explain可以幫我們更詳細的了解MySQL查詢的執行計劃,用法也很簡單Explain 后面跟上SELECT語句即可。執行完之后,會顯示一行有多個列的記錄,可能很多人和我一樣,對EXPLAIN里面字段的含義,并沒有深入的去了解過,處于一知半解的狀態,只知道一些最常見的。
? 下面我根據MySQL官方文檔,查閱了很多資料,再結合我自己的理解,對EXPLAIN的字段和值做了詳細的描述,在總結過程中,也發現了自己的很多知識漏洞,很多時候,總是會想當然的認為,這個就是對的,并沒有嚴密的邏輯驗證,大腦喜歡偷懶,正所謂好記性不如爛筆頭,寫的過程也是對自己知識點掌握程度的批判和考驗。
關于EXPLAIN
? EXPLAIN返回一行記錄,通過Explain可以獲取到很多信息,如:不同表的查詢順序,查詢用了哪些表,能使用哪些索引以及真正用到了哪些索引,用了哪種連接類型,是否有臨時表和文件排序等。這些因素對查詢的效率有直接的相關,想要使查詢更高效,需要對這些條件做一個好的優化。
? EXPLAIN有12個字段,每個字段對查詢優化的權重比不一樣,也就是說并不是所有字段都很重要。type,key,Extra字段相對其它字段來說,對查詢效率的影響更大,優化查詢的時候,先把注意力放到這些字段會比其它字段來得更加直接有效,下面開始具體內容。
EXPLAIN語法
以user_info表為例:
explain select * from `user_info` where uid = 5
結果:
id
select_type
table
partitions
type
possible_keys
key
key_len
ref
rows
filtered
Extra
1
SIMPLE
user_info
NULL
const
PRIMARY
PRIMARY
8
const
1
100.00
NULL
EXPLAIN字段說明
注: 標注星號的字段為重點
id:
SELECT語句的標識符,代表SELECT查詢在整個查詢中的序號。這個值也可能為NULL,如果這一行是UNION的結果。
select_type:
SELECT查詢的類型,該類型的值有11種類型。例如,示例中的值為SIMPLE,表示該查詢是一個簡單的查詢(即:沒有子查詢和UNION)。
table:
大多數情況下表示輸出行所引用的表名,它也可能是下列值之一:
partitions:
只對分區表有意義。意思是查詢所匹配到的分區,如果該表為非分區表,則它的值為NULL。
*type:
查詢的join類型,注意單表查詢也被當做join的特例,并不一定要兩張表。連接類型詳情下面會詳細介紹。
possible_key:
possible_key列是指,在查詢中能夠被MySQL用到的索引,但在實際情況中,不一定會被全部用到,這取決于MySQL優化器的選擇,假設possible_key有A,B,C,3個索引,優化器經過分析認為A索引不需要用,那么實際執行的時候只會用到B,C索引。實際應用中,該列經常幫我們對SQL查詢進行優化,如果它的值為NULL,說明沒有能被用到的索引,這種情況下,需要調整SQL語句和優化表的索引。
*key:
查詢中實際用到的索引,要注意,該列的值可能包含possible_key列中沒有出現的索引,當查詢滿足覆蓋索引的條件時,possible_keys列為NULL,索引僅在key列顯示,MySQL只需要掃描索引樹,不用到實際的數據行檢索即可得到結果,查詢會更高效,Extra列顯示USING INDEX,則證明使用了覆蓋索引。 也可以通過FORCE INDEX,USE INDEX或IGNORE INDEX來強制使用或忽略possible_key列中的索引。
覆蓋索引概念:
如果索引包含所有滿足查詢需要的數據的索引成為覆蓋索引(Covering Index)。
假設有一個user表,假設索引A包含了col1,col2,col3三個字段,criteria為標準條件。
Query 1:
select * from user where criteria
Query 1使用了索引查詢,獲取到數據行的主鍵,但是仍然需要根據主鍵值掃描實際的數據行。
Query 2:
select `col1`,`col2` where criteria
Query 2中,索引A已經包含了它需的字段,也就是說Query 2不用再去實際的數據行獲取數據了,只要掃描完索引樹就行了,這樣就省了一個步驟,索引樹往往比實際的數據表小,所以效率很高,這就是覆蓋索引。
key_len:
實際用到的索引字段長度,越短越好。
ref:
ref列顯示哪個列或者常數和索引比較篩選出結果。
rows:
rows列表示MySQL認為執行查詢必須檢查的行數,對Innodb表來說,這是一個預估值,可能并不是確切的值。
filtered:
? filtered的意思是,首先MySQL利用索引,例如,用range范圍掃描出符合的行,如果掃描符合條件的估計值是100行,rows顯示估計的值就是100,這一步是存儲引擎根據索引篩選后的值,然后在Server層根據其余的WHERE條件過濾。
? 被過濾器過之后,符合條件的還剩下20行,也就是剩下20%,20%就是filtered中的值。很顯然,直接在存儲引擎層篩選出20行比先篩選出100行再過濾要更好,通常情況下,filtered的值越大可能意味著索引越好。
? 另一方面看,你也可以完全忽略filtered,因為這個值在大多數情況下只是一個不準確的估計,應該把注意力放到優化其它更有用的字段上,尤其是type,key,Extra。例如:盡量避免filesort排序,使用索引排序。或者有一個更好的type值,對性能的提升是非常巨大的,這種情況,即使filtered的值低也沒關系。假設一個查詢A, type=all,filtered=0.1%。那么首要先關注type字段,可通過添加索引來優化,可以先不管filtered。
? 所以對這個值不需要太認真,即使100%也不意味著索引一定好,反過來也不一定說明索引差,type比它更能說明索引的好壞。
*Extra:
這個列包含Mysql解決查詢的詳細信息,詳情見下方。
EXPLAIN字段值說明:
select_type:
select_type 值
描述
SIMPLE
簡單的SELECT查詢(沒有UNION和子查詢)
PRIMARY
一個需要union操作或者含有子查詢的select,位于最外層的單位查詢的select_type即為primary。且只有一個
UNION
UNION連接的select查詢,除了第一個表外,第二個及以后的表select_type都是union
DEPENDENT UNION
與union一樣,出現在union 或union all語句中,但是這個查詢要受到外部查詢的影響
UNION RESULT
UNION之后的結果集
SUBQUERY
除了from字句中包含的子查詢外,其他地方出現的子查詢都可能是subquery
DEPENDENT SUBQUERY
與dependent union類似,表示這個subquery的查詢要受到外部表查詢的影響
DERIVED
FROM字句中出現的子查詢。語法:SELECT ... FROM (subquery) [AS] tbl_name ...
MATERIALIZED
被物化的子查詢
UNCACHEABLE SUBQUERY
對于外層的主表,子查詢不可被物化,每次都需要計算(耗時操作)
UNCACHEABLE UNION
UNION操作中,內層的不可被物化的子查詢(類似于UNCACHEABLE SUBQUERY)
通過 **物化 ** 優化子查詢的原理:
? 優化器使用物化的方式能讓子查詢更高效的執行,類似緩存技術,把第一次查詢的結果存起來,避免多次的耗時操作,同時也有它自身的限制,不是所有子查詢都能被物化的。物化技術把子查詢產生的結果放在一個臨時表中,如果數據量小的話,通常是在內存中完成,數據大的時候就降級到磁盤進行,速度也會慢很多。首先,MySQL得到子查詢的結果,然后把結果放到臨時表中,在隨后的任何時間,當需要這個結果時,MySQ就再次引用這個臨時表,不需要再執行計算了。優化器可能會使用哈希索引(復雜度為O(1),很快)來快速且低成本的查找表,這個索引是唯一的,避免了重復,能使表更小。
SELECT * FROM t1
WHERE t1.a IN (SELECT t2.b FROM t2 WHERE where_condition);
type(連接類型):
system
當表只有一行數據的時候,這是const連接類型的特例。
const
表中最多只有一行匹配,在查詢開始時被讀取。因為只有一行,該行中列的值可以被優化器的其余部分視為常量。const表非常快,因為他們僅被讀取一次。將PRIMARY KEY 或 UNIQUE INDEX索引和常量值比較時,會使用const。例如:
SELECT * FROM tbl_name WHERE primary_key=1;
SELECT * FROM tbl_name
WHERE primary_key_part1=1 AND primary_key_part2=2;
eq_ref
假設A JOIN B,B表讀取A表的各個行組合的一行時,通過B表的PRIMARY KEY或UNIQUE NOT NULL索引列連接時,優化器會使用eq_ref類型,這是除了system和const之外最快的JOIN類型。
舉例說明:
表tableA,有(id,text)字段,id為PRIMARY KEY,A表數據為:
id
text
1
HELLO
2
THANK
表tableB有(id,text)字段,id為PRIMARY KEY,B表數據為:
id
text
1
WORLD
2
YOU
現在通過JOIN將兩個表關聯起來
SELECT A.text,B.text
FROM tableA AS A,tableB as B
WHERE A.id=B.id
這個連表查詢是非常快的,因為在A表中掃描的每一行,在B表中也僅一行滿足條件。
ref
? A JOIN C時,A表中的每一行不是唯一的,對單表查詢也一樣,有多個滿足條件的行,查詢的KEY是單個索引或復合索引的最左前綴(不是唯一索引和主鍵),也就是說C表的id是一個非唯一索引。這種情況下,優化器會使用ref優化,如果只有少部分行(rows)滿足條件,這個連接類型(join type)是很好的。ref用于索引的比較操作,注意:僅對于=,<=> 操作有效,對于>,
舉例說明:
現在有tableC,id為索引,不唯一。數據為:
id (非唯一索引)
text
1
HANGZHOU
1
SHANGHAI
現在通過LEFT JOIN將A和C關聯起來:
SELECT A.text,C.text
FROM `tableA` AS A
LEFT JOIN `tableC` AS C ON A.id=C.id
這個JOIN不像之前的那么快,因為在表A中掃描的每一行,在表C中可能有很多行滿足條件,C的id不是唯一索引。
fulltext
使用了全文索引,Innodb不支持全文索引。
ref_or_null
如果一個查詢的WHERE子句中包含colA IS NULL的條件,但是colA已經被聲明為NOT NULL,此時優化器會使用ref_or_null類型。
SELECT * FROM ref_table
WHERE key_column=expr OR key_column IS NULL;
index_merge
? 在MYSQL5.0之前是沒有索引合并功能的,假設A表有3個單獨的索引col1 ,col2,col3,然后執行如下SQL:
SELECT * FROM A WHERE col1=1 AND col2=2 AND col3=3
實際查詢中只有一個索引能被用到,這種情況,只能通過建立復合索引(col1,col2,col3)才能在索引中用到所有字段。
? 5.0之后有了索引合并,當檢索數據行時出現多個范圍掃描條件時,在滿足索引合并前提條件時(單個索引覆蓋WHERE條件的字段),MySQL優化器可能會使用索引合并(不一定),首先分別對多個索引進行掃描,然后合并來自單個表的掃描結果,它不能合并多個表的掃描結果,合并的方式有3種:
unions:索引取并集
intersections:索引取交集
Sort-Union:先對取出的數據按主鍵排序,再取并集
索引合并條件:
WHERE子句中的范圍條件,WHERE中出現字段必須被索引覆蓋,如果colA沒添加索引,則只會對colB和colC進行索引合并,Extra字段顯示Using intersect(colB,colC);,type為index_merge,則說明用到了索引合并。
WHERE colA = const1 AND colB = const2 AND colC = const3
Innodb表中的主鍵的任何范圍條件,>,等。
SELECT * FROM innodb_table
WHERE primary_key < 10 AND key_col1 = 20;
? 滿足了條件,MYSQL會選擇索引行數最少的字段對索引結果進行合并,最終使用哪個索引字段來合并也不一定,也可能不使用合并,這取決于優化器,如果優化器認為沒必要使用索引合并優化,就會使用其它優化,也許會選擇type 為range或更高效的ref的優化。
? 當優化器決定使用索引合并優化,如果WHERE條件用AND連接,優化器會使用INTERSECTIONS合并算法,對多個索引掃描的結果取交集。如果用OR連接,優化器會選擇UNIONS或SORT-UNIONS合并算法,對多個索引掃描的結果取合集,SORT-UNIONS和UNIONS的主要區別是,前者在掃描完數據時,需要先對數據按主鍵排序,再取它們的合集。
? 在WHERE子句中使用AND時,使用復合索引比索引合并更高效,因為復合索引只用一個索引篩選,沒有匹配合并的過程,這個過程節省了很多時間。
? 在使用OR時,復合索引是不起作用的,這種情況下,使用UNIONS索引合并效果更好。如果不想使用某種索引合并,也可以選擇關閉。可通過optimzer_switch系統變量查看各個索引合并的開啟狀況。如下:
SELECT @@optimizer_switch
索引合并算法的默認都是開啟的,可以通過關閉某個合并算法。例如:
SET optimizer_switch = 'index_merge_intersection=off'
unique_subquery
這種類型是eq_ref類型在子查詢中的替代類型。例如
SELECT * FROM A WHERE
value IN (SELECT id FROM B WHERE some_expr)
B表中的id在A表中有唯一對應的記錄。
range
? 在WHERE子句中,執行>,,=,BETWEEN,IN() 等操作時,MySQL可能會(不一定)使用range類型,Explain中key列的值就是實際用到的索引,key_len是它們中最長的索引的長度。如果優化器認為使用索引篩選沒有全表掃描來得及,例如:條件篩選后的行占全表的50%以上,即使有索引可用,優化器也會選擇全表掃描,即type=ALL。
? 為什么呢?解釋這個問題之前,需要先了解幾個概念。對Innodb表來說,每個表都有一個聚簇索引,InnoDB的聚簇索引實際上在同一個結構中保存了B-Tree索引和數據行信息。因為無法把數據行存放在兩個不同的地方,所以一個表只能有一個聚簇索引。二級索引存儲的是記錄的主鍵,而不是數據存儲的地址,索引數據和存儲數據是分離的,唯一索引、普通索引、前綴索引等都是二級索引。實際上,InnoDB在查詢任何數據時,最后都是通過主鍵來查詢的。首先我們根據索引條件在索引樹上掃描出對應的主鍵值。然后根據這個值去聚簇索引總超找到對應的行(如果是覆蓋索引則省略這一步)。
? 在某些情況下,索引條件掃描出的數據行非常大,可能占了全表的50%,此時再根據主鍵找到對應的數據塊是不劃算。主鍵的BTree查找屬于文件的隨機搜索,但是如果隨機搜索文件數據的目的是為了查找一半的數據,這并不是最優化的,只要對數據文件進行大量的順序讀寫要更快,這種情況下,索引會被忽略。
index
? index類型和ALL類型幾乎相同。有兩種情況:
若SELECT中列全部被索引覆蓋,所需要的數據可以直接在索引中讀取,MySQL只需對索引樹進行掃描,這通常比掃描實際數據行要快,因為索引樹通常比數據表更小,這種情況下,Extran的值會顯示USING INDEX。
使用索引中讀取的主鍵值,按索引順序對全表進行掃描,此時Extra中沒有USING INDEX。
ALL
對表的每一行進行掃描,這是最糟糕的情況。一般,你可以通過添加索引來避免這種情況發生。
Extra列值的含義:
? Extra列包含了MySQL處理查詢的一些額外信息,下面的列出了Extra中可能出現的值,如果你想讓查詢盡可能的快,應該注意下Extra字段中是否出現了using filesort 和using temporary。下面只列除了在實際應用中經常會出現,相對比較重要的一部分,若描述的不夠詳細,可查看MySQL官方文檔。
const row not found
SELECT * FROM A
如果A表為空,則會出現改值。
DISTINCT
mysql在尋找不同的值,當它找到第一個匹配的行之后,就停止搜索更多的行了。例子:
no matching row in const table
用唯一索引或者主鍵查詢時,沒有匹配到的數據。
Not exists
MySQL優化了LEFT JOIN,一旦它找到了匹配LEFT JOIN標準的行,就不再搜索了,。例如:
SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id
WHERE t2.id IS NULL;
Using filesort
? 這個值表示,MySQL必須對檢索到的結果進行額外的排序。排序是按照連接類型遍歷所有行并存儲排序鍵和指向行的指針,以匹配滿足where子句條件的所有行,然后對鍵進行排序,并按排序順序檢索行。根據不同情況,MySQL會選擇不同的排序算法,在數據比較小的時候,MySQL會利用排序緩沖區作為優先級隊列將結果在內存中排序,否則只能通過合并文件的方式合并,那會慢很多,排序緩沖區的大小取決于sort_buffer_size變量的大小。
? 總之,當看到filesort的時候就應該引起重視,通過優化索引來避免額外的文件排序,這對性能影響是很大的。
Using index
單個索引覆蓋了SELECT的所有列(即:覆蓋索引),不需要對實際的數據行進行掃描。
Using index condition
? Index Condition Pushdown (ICP)是MySQL 5.6 版本中的新特性,是一種在存儲引擎層使用索引過濾數據的一種優化方式。當關閉ICP時,index 僅僅是data access 的一種訪問方式,存儲引擎通過索引回表獲取的數據會傳遞到MySQL Server 層進行where條件過濾。
? 當打開ICP時,如果部分where條件能使用索引中的字段,MySQL Server 會把這部分下推到引擎層,可以利用index過濾的where條件在存儲引擎層進行數據過濾,而非將所有通過index access的結果傳遞到MySQL server層進行where過濾.
優化效果:ICP能減少引擎層訪問基表的次數和MySQL Server 訪問存儲引擎的次數,減少io次數,提高查詢語句性能。
Using index for group-by
和USING INDEX很相似,區別是,當查詢語句中含有DISTINCT和GROUP BY操作時,僅需訪問索引樹,不需要訪問實際的表時,使用該優化。
``Using sort_union(...),Using union(...),Using intersect(...)`
當查詢產生索引合并時會顯示該值,type為index_merge。
Using temporary
為了處理查詢,MySQL必須建立一個臨時表才能產生結果。典型的情況是,在使用GROUP BY和ORDER BY子句時,兩者使用了不同的列會導致產生臨時表。
Using where
using where 是指使用WHERE或ON子句,MySQL Server層收到存儲引擎返回的結果時,需要對結果再次過濾,不需要返回所有結果,注意LIMIT不算限制條款。如果沒有用到索引using where只是說明,使用了顧慮條件過濾。
參考
總結
以上是生活随笔為你收集整理的MySQL怎么打开explain_MySQL干货之-利用EXPLAIN优化查询的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java怎么编写软键盘_安卓手机软键盘操
- 下一篇: java 打印 排序 数字塔_求助,用循