為什么要做性能分析
你有沒有這樣的情況。
面對一個你沒怎么寫過的、復雜的業務,你構思了很久,終於開始敲下了第一段代碼。
寫的過程迷迷糊糊,有的時候還能把自己搞暈了。
但你還是終於把它寫完了。
但是點擊一運行,完了,有bug。
怎么辦?
debug的方式有很多,控制台打印是一種。
通過控制台打印的信息,我們能根據反饋去修改代碼,直到代碼能正常運行為止。
其實建索引也是一樣的。
上篇帖子《淺談sql索引》,說過索引的難點在於針對一個具體的表去做出最合適的索引。
因為這不只要看你表里有什么內容,更多要看你的業務,你的業務會經常根據哪些關鍵詞查找。
代碼我們可以天天寫,索引不能天天建吧。
所以多數情況下,因為不熟悉,我們一開始建立的索引往往都不是最好的,唯有根據反饋去調整索引,才能做出一個最合適這個表的索引。
今天要分享的就是怎么去看懂這個反饋,即怎么去做性能分析。
怎么做性能分析
使用EXPLAIN關鍵字!
使用EXPLAIN關鍵字可以知道MySQL是如何處理你的SQL語句的,分析你的查詢語句或是表結構的性能瓶頸。
但EXPLAIN並不能直接反饋我們建的索引的好壞。
用法是這樣的,我們建好了索引,拿着業務中最常用的幾句SQL語句來EXPLAIN一下,如果反饋的效果好,那么建立的索引就是最適合這個表的,反之則需要改進。
要不改索引,要不改SQL。
EXPLAIN玩法
語句
explain 要查看的sql語句
(橫表)或explain 要查看的sql語句\G
(豎表)
一個具體的例子
各個字段解釋
從上圖中我們可以看到,explain出來的信息有以下字段:
id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
---|
你仔細看看,這么長是不是有點像工資條?
其中各個字段代表的意思是這樣的:
-
id: 查看表的讀取順序。
咱們上圖中的例子只查詢了一個表,但若是多表聯合查詢,則有: id相同的話,執行順序由上至下; id不同的話,id值越大優先級越高;
-
select_type: 顯示區別聯合查詢、子查詢、普通查詢等。
以下為其可能的值,以及對應所代表的信息: SIMPLE -- 簡單的select查詢,不包含子查詢或union; PRIMARY -- 查詢中若包含任何復雜的子部分,最外層查詢則被標記為PRIMARY; SUBQUERY -- 在select或where包含的子查詢; DERIVED -- 在from列表包含的子查詢被標記為DERIVED(衍生),MySQL會遞歸執行這些子查詢,把結果放在臨時表里; UNION -- 若第二個select出現在union后,則被標記為union;若union包含在from子句的子查詢中,外層select將被標記為:DERIVED; UNION RESULT -- 從union表獲取結果的select;
-
table: 顯示表名。
-
type: 顯示查詢用了何種類型。
以下為其可能的值,以及對應所代表的信息: system -- 表只有一行記錄(等於系統表),這是const類型的特例,平時不會出現,可以忽略不記。 const -- 表示通過索引一次就找到了,const用於比較primary key(主鍵)或者unique(唯一)索引。因為只匹配一行數據,所以很快,如果將主鍵置於where列表中,mysql就能將該查詢轉換到一個常量。 eq_ref -- 唯一性掃描索引,對於每個索引鍵,表中**剛好只**有一條記錄與之匹配。常用於主鍵或唯一索引掃描。 ref -- 非唯一性掃描索引,返回匹配某個單獨值的所有行。上面`eq_ref`的多值情況。如where age=1,age=1的數據剛好只有一行,便顯示eq_ref,age=1的數據有多行,便顯示ref。 range -- 只檢索給定范圍的行,使用一個索引來選擇行。如果type列的值為`range`,key列便會顯示使用了哪個索引。一般就是在where語句中出現了between、<、>、in等的查詢。這種范圍掃描查詢比全表掃描要好。 index -- Full Index Scan(掃描全部索引),index與ALL區別為index類型只遍歷索引樹。都是讀全表,但是index是從索引中讀取,all是從硬盤中讀,而且索引文件通常比數據文件小。 all -- Full Table Scan(掃描全表),遍歷全表來找到匹配的行,即索引完全沒用上。 從最好到最差依次是:system>const>eq_ref>ref>range>index>ALL 細節: 1. type 是 ALL,當數據到達百萬以上一定要優化。 2. 一般來說,如果要優化得保證查詢至少達到range級別,最好達到ref。
-
possible_keys和key
possible_key: 顯示可能應用在這張表中的索引,一個或多個。理論上可能被使用的索引,但不一定被查詢實際使用。 key: 實際使用的索引,如果為null,則沒有使用索引。 這兩列有四種情況: 1. possible_key有值,key有值:正常,有的時候前者有多個值但后者只有一個也正常。 2. possible_key有值,key無值:**索引失效了,出現問題了**。 3. possible_key無值,key有值:條件查詢(如where)沒有用到索引或沒有條件查詢,但查詢的列(select后面的字段)剛好順序、數量和索引一致。 4. possible_key無值,key無值:正常,就是你沒建索引。
-
key_len: 表示索引中使用的字節數。
可通過該列計算【查詢中使用的索引的長度】,在查詢結果一樣的情況下,該值越小越好。 key_len顯示的值為索引字段的最大可能長度,而非實際使用長度,即通過表定義計算而得,不是通過表內檢索而得。 假設你建立復合索引(col1,col2),如果【通過col1條件查詢】和【通過col1和col2條件查詢】的結果一樣,那么前者比較好,因為只需要用一個字段,key_len的值會比較小,上面也說過是通過表定義的長度來決定key_len的值。
-
ref: 顯示key列中索引參照的值。
有兩種可能的值,以及對應所代表的信息: 庫名.表名.字段名 -- 表示索引參照的值是哪個庫的哪個表的哪個字段; const -- 表示索引參照的值是常量,一般是where id=1這樣才會出現;
-
rows: 根據表統計信息以及索引選用情況,大致估算出找到要查找的記錄需要讀取的行數。
-
Extra: 十分重要的額外信息。
以下為其可能的值,以及對應所代表的信息: Using filesort -- 說明mysql完全或部分沒有按照你所建的索引排序,比較需要優化了。MySQL無法利用索引完成的排序操作稱為“文件排序”; Using temporary-- 使用了臨時表保存中間結果,mysql對查詢結果排序時使用臨時表。這也比較需要優化,因為臨時表的創建和刪除都是比較費性能的,常見於order by和group by; Using index -- 表示相應的select操作中使用了覆蓋索引,避免了訪問表的數據行,效率不錯。如果同時出現Using where,表明索引被用來執行索引鍵值的查詢,如果沒有出現,表明索引被用來讀取數據而非執行查找動作; Using where -- 使用了where過濾; Using join buffer -- 使用了連接緩存,如果總是出現這個字段,可以去配置文件中適當調大這個值; Impossible where -- where子句的值總是false,不能用來獲取任何元組; Select tables optimized away -- 在沒有group by子句的情況下,基於索引優化MIN/MAX操作或者對於MyISAM存儲引擎優化count(*)操作,不必等到執行階段再進行計算,查詢執行計划生成的階段即完成優化; distinct -- 優化distinct操作,在找到第一組匹配的元組后馬上停止找相同值的動作。 注意: 1. 使用group by等排序時,如果有用到索引,最好嚴格按照索引的順序來,比如,存在復合索引(col1,col2),排序時如果跳過col1,直接使用col2排序,會導致出現Using filesort、Using temporary等比較嚴重的問題。 2. 盡量使用覆蓋索引,select后面的列名完全與建立的索引順序、數量一致。這樣可以直接使用索引讀取數據,避免讀取表的數據行。
案例
最后來看一個簡單的案例,我會先放題目,再放思路,最后放答案。
題目
要求是寫出SQL的執行順序。
思路
-
首先看id列,id越大優先級越高,索引從id為4的那一行開始看,這一行的table為t2,即查詢的是t2,所以最先查找的反而是最后的部分
select name,id from t2
。 -
id為3的這一行,table是t1,所以查的是
select id,name from t1 where other_column=''
;這一行后面的Extra列的Using where
同樣佐證了這點。 -
id為2的這一行,table是t3,所以查的是
select id from t3
;另外,key列是primary說明用到了主鍵作為索引,Extra列的Using Index
表示用到了覆蓋索引(即索引用在了select后面)。 -
id為1的這一行,table是
,意思是根據【id為3的那一行的查詢結果】來查詢(對應的你也可以看到id為3的那一行的select_type列為Derived),所以查的是 select d1.name,(select id from t3)d2 from (select id,name from t1 where other_column = '') d1
-
id為NULL這一行,table是<union1,4>,意思是根據【id為1和4的那兩行的查詢結果】來實現union查詢。
答案
1.select name,id from t2
2.select id,name from t1 where other_column=''
3.select id from t3
4.select d1.name,(3.result)d2 from (2.result) d1
5.(4.result) union (1.result)
最后還是建議結合前面每個字段的解釋,有自己的一個思考過程是最好的。
最后
今天說的是如何看explain的結果。
就好像咱們控制台打印信息是為了看代碼運行的情況一樣,看完了,如果有bug,還要動手改的。
而我們今天只是說如何看而已,下一步,就是如何改了。