一直對SQL優化的技能心存無限的向往,之前面試的時候有很多面試官都會來一句,你會優化嗎?我說我不太會,這時可能很多人就會有點兒說法了,比如會說不要使用通配符*去檢索表、給常常使用的列建立索引、還有創建表的時候注意選擇更優的數據類型去存儲數據等等,我只能說那些都是常識,作為開發人員是必須要知道的。但真正的優化並不是使用那些簡單的手法去完成實現的,要想知道一條SQL語句執行效率低的原因,我們可以借助MySQL的一大神器---"EXPLAIN命令",EXPLAIN命令是查詢性能優化不可缺少的一部分,本文在結合實例的同時會總結explain命令的使用及相關參數的說明。
首先說說我這次浴火重生的優化初衷吧,上個月在公司完成的統計模塊中,其中就有幾條SQL語句執行的速度稍微有點慢,心里一直留了一道坎。直到昨天晚上在家看了一篇文章,是關於MySQL優化的對EXPLAIN命令的詳解,所以今天一到公司就想着把之前那些SQL語句的病趕緊給看看,雖然,我沒有達到那種秒查的效果,但是優化的效果還是有明顯的提升。
業務場景:
分區統計XXX省每月上傳數據的企業數量,何為企業是否是未上傳數據,即專門存放上傳數據的數據表中沒有記錄的為未上傳數據的企業,如果有那么代表已經上傳數據。
下面是我之前寫的SQL語句(未優化前的),它執行的時間是2.318sec,並且使用EXPLAIN命令進行分析:
首先說說EXPLAIN命令查詢后打印的數據列它們各個列代表的意思:
1、id :該列的值是用來順序標識整個查詢中SELELCT 語句的執行順序,在嵌套查詢中id越大的語句越先執行,該值可能為NULL。個人建議,可以在分析一條很長的SQL語句時可以依照它的值來按順序進行切割分析優化。
2、select_type :表示當前select查詢的類型,該列可能出現的值還有如下情況;
3、table :對應行正在訪問哪一個表,表名或者別名(注意:MySQL對待這些表和普通表一樣,但是這些“臨時表”是沒有任何索引的);
-
關聯優化器會為查詢選擇關聯順序,左側深度優先
-
當from中有子查詢的時候,表名是derivedN的形式,N指向子查詢,也就是explain結果中的下一列
-
當有union result的時候,表名是union 1,2等的形式,1,2表示參與union的query id
4、type :該列的值代表的含義是訪問數據表的檢索類型,也是最為重要的優化指標之一,它的好壞情況依次是 system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL ,一般來說,得保證查詢至少達到range級別,最好能達到ref級別,下面是各個值代表的意思。個人建議:可以在分析一條很長的SQL語句時,盡量讓每個拆分的檢索子句都到理想的優化級別;
5、possible_keys :顯示查詢使用了哪些索引,表示該索引可以進行高效地查找,但是列出來的索引對於后續優化過程可能是沒有用的,也就是說該索引在查找的時候未必真正的使用上。
6、key :該列表示在檢索時實際決定使用的鍵(索引)。如果沒有選擇索引,鍵是NULL。要想強制MySQL使用或忽視possible_keys列中的索引,在查詢中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。
7、key_len :該列顯示MySQL決定使用的鍵長度。如果鍵是NULL,則長度為NULL。使用的索引的長度。在不損失精確性的情況下,長度越短越好 。
8、ref :該列表示使用哪個列或常數與key一起從表中選擇行,個人翻譯:就是當前檢索中的語句與哪個表中的列聯合查找數據的。
9、rows :該列顯示MySQL認為它執行查詢時必須檢查的行數。注意這是一個預估值。個人建議:該值如果比整表總記錄數越低,則越好。
10、Extra :該列的值是EXPLAIN輸出中另外一個很重要的列,該列顯示MySQL在查詢過程中的一些詳細信息,MySQL查詢優化器執行查詢的過程中對查詢計划的重要補充信息。我們通常根據該列的值來判斷SQL語句是否需要優化;
根據上面的知識分析:
我通過MySQL EXPLAIN分析的思路是這樣的:通常首先要根據id的值確定當前檢索語句是何時執行的,注意分析的時候按順序分析,其次在根據type列的值來判斷當前檢索語句是否需要優化,最后看決定性的一點就是根據Extra的值,上面表格中也說了,如果看到Using temporary一般就需要優化了。
因為我上面的那條語句是一個子查詢,所以我首先根據id的值找到最先執行的檢索語句,也就是嵌套在最內層的那條等值查詢語句,它分別使用等值條件去連接企業表和上傳數據表篩選出符合條件的數據,但是使用EXPLAIN命令分析得出,這條檢索語句並不是真正的高效,在掃描org表的時候進行了全表數據連接而不是有條件的去刷選連接,而且在等值連接的時候並未真正使用主鍵索引去等值連接,再回過頭來仔細想想我們的業務,就是拿着info表中的數據去org表中進行匹配,當然全表掃描info是避免不了的,但是org表不一定全部都掃描啊,所以我試着用左連接替代之前的等值連接,果然效果達到了,info表進行了全表數據匹配去連接org表中的數據,並且在匹配的時候org表的主鍵索引也真正使用到了,級別達到了eq_ref,至於info表的ALL級別,毋庸置疑業務需要必須就是要去掃描整表的;最后看到最外層的檢索語句也未必是高效的,它關聯的地區表也進行了全表數據匹配,但是我要的查詢結果是根據子查詢結果來得出的,肯定不比子查詢結果的數據多,所以我將子查詢結果作為左表去匹配地區表中的數據,果然,由ALL級別變成range級別,檢查的行數也由3646減少到了15,經過分析優化執行速度提升1秒多,優化級別基本達到理想的狀態,由於本人能力有限,只能做到這步:
以上都是本人經過實踐得出,並非亂寫,如果有地方總結不到位,希望各位留言補正,共同學習,共同進步,一直在優化的路上。