Spring+SpringMVC+MyBatis+easyUI整合優化篇(十二)資料層優化-explain關鍵字及慢sql優化
本文提要
從編碼角度來優化資料層的話,我首先會去查一下專案中執行的sql語句,定位到瓶頸是否出現在這裡,首先去優化sql語句,而慢sql就是其中的主要優化物件,對於慢sql,顧名思義就是花費較多執行時間的語句,它帶來的影響也比較惡劣,首先是執行時間過長影響資料的返回速度,其次,慢sql的長時間執行也會消耗和佔用mysql的系統資源,影響其他的sql語句執行,過多的慢sql極其影響效能,如果系統流量或者併發量較大的情況下,過多的執行慢sql很有可能造成mysql的死鎖以致於mysql服務無法正常使用。 druid整合到專案中以及druid監控的開啟已經持續了一段時間,因此對於慢sql的監控和整理也大致有了一些結果,本篇文章就試著從日誌檔案和監控面板中找出幾條慢sql並進行優化。
優化步驟
總結了一下,大致步驟如下:
- 定位優化物件的效能瓶頸;
- 明確優化目標;
- 從explain入手分析;
- 找到優化方法;
找出慢sql
首先進入druid監控後臺,檢視一下這幾天的執行日誌後,慢sql的大致情況,如圖:
從監控後臺看到的資料只是一個粗略的統計,是一個總覽記錄,想要看到詳細的執行記錄及其中的慢sql統計可以通過日誌檔案,這個功能也已經整合到專案中,直接在tomcat的logs目錄即可檢視。
日誌檔案內容節選:
//1.圖片表查詢sql [10:13:37] StatFilter - slow sql 1572 millis. select * from ssm_picture WHERE type = ? and grade = ? limit ?,? ["1","1",0,10] ... //2.更新文章表sql [14:19:12] StatFilter - slow sql 1926 millis. update ssm_article set article_title=?,article_content=?, add_name=? where id=? ["11","<p>1324354657usdfghjnkm,zxvb nm,,fgfhjtfggggggggggggggggggg<br/></p>","22","1033"] ... //3.文章表查詢sql [15:07:04] StatFilter - slow sql 1672 millis. select * from ssm_article limit ?,? [0,10]
日誌的記錄格式為 [執行時間] -慢sql執行耗時 ,sql語句,其實日誌中記錄是挺多的,去重之後從日誌檔案中單獨選了幾條比較典型的sql語句進行優化。
explain關鍵字
explain關鍵字一般放在SELECT查詢語句的前面,用於描述MySQL如何執行查詢操作、以及MySQL成功返回結果集需要執行的行數。explain 可以幫助我們分析 select 語句,讓我們知道查詢效率低下的原因,從而改進我們查詢,讓查詢優化器能夠更好的工作。
用法:
結果集說明如下:
項 |
說明 |
---|---|
id |
MySQL Query Optimizer選定的執行計劃中查詢的序列號。表示查詢中執行select子句或操作表的順序,id值越大優先順序越高,越先被執行。id相同,執行順序由上至下。 |
select_type 查詢型別 |
說明 |
---|---|
SIMPLE |
簡單的select查詢,不使用union及子查詢。 |
PRIMARY |
最外層的select查詢。 |
UNION |
UNION 中的第二個或隨後的 select查詢,不依賴於外部查詢的結果集。 |
DEPENDENT UNION |
UNION中的第二個或隨後的 select查詢,依 賴於外部查詢的結果集。 |
SUBQUERY |
子查詢中的第一個select查詢,不依賴於外部查詢的結果集。 |
DEPENDENT SUBQUERY |
子查詢中的第一個select查詢,依賴於外部查詢的結果集。 |
DERIVED |
用於from子句裡有子查詢的情況。MySQL會遞迴執行這些子查詢,把結果放在臨時表裡。 |
UNCACHEABLE SUBQUERY |
結果集不能被快取的子查詢,必須重新為外層查詢的每一行進行評估。 |
UNCACHEABLE UNION |
UNION中的第二個或隨後的select查詢,屬於不可快取的子查詢。 |
項 |
說明 |
---|---|
table |
輸出行所引用的表 |
type 顯示連線使用的型別,按最優到最差的型別排序 |
說明 |
---|---|
system |
表僅有一行(=系統表)。這是const連線型別的一個特例。 |
const |
const用於用常數值比較PRIMARY KEY時。當查詢的表僅有一行時,使用System。 |
eq_ref |
const用於用常數值比較PRIMARY KEY時。當查詢的表僅有一行時,使用System。 |
ref |
連線不能基於關鍵字選擇單個行,可能查詢到多個符合條件的行。叫做ref是因為索引要跟某個參考值相比較。這個參考值或者是一個常數,或者是來自一個表裡的多表查詢的結果值 |
ref_or_null |
如同ref, 但是MySQL必須在初次查詢的結果裡找出null條目,然後進行二次查詢。 |
index_merge |
說明索引合併優化被使用了。 |
unique_subquery |
在某些IN查詢中使用此種類型,而不是常規的ref:value IN (SELECT primary_key FROM single_table WHERE some_expr) |
index_subquery |
在某些IN查詢中使用此種類型,與unique_subquery類似,但是查詢的是非唯一性索引:value IN(SELECT key_column FROM single_table WHERE some_expr) |
range |
只檢索給定範圍的行,使用一個索引來選擇行。key列顯示使用了哪個索引。當使用=、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN或者IN操作符,用常量比較關鍵字列時,可以使用range。 |
index |
全表掃描,只是掃描表的時候按照索引次序進行而不是行。主要優點就是避免了排序,但是開銷仍然非常大。 |
all |
最壞的情況,從頭到尾全表掃描。 |
項 |
說明 |
---|---|
possible_keys |
指出MySQL能在該表中使用哪些索引有助於查詢。如果為空,說明沒有可用的索引。 |
項 |
說明 |
---|---|
key |
MySQL實際從possible_key選擇使用的索引。如果為NULL,則沒有使用索引。很少的情況下,MYSQL 會選擇優化不足的索引。這種情況下,可以在SELECT語句中使用USE INDEX (indexname)來強制使用一個索引或者用IGNORE INDEX(indexname)來強制MYSQL忽略索引 |
項 |
說明 |
---|---|
key_len |
使用的索引的長度。在不損失精確性的情況下,長度越短越好。 |
項 |
說明 |
---|---|
ref |
顯示索引的哪一列被使用了 |
項 |
說明 |
---|---|
rows |
MYSQL認為必須檢查的用來返回請求資料的行數 |
extra 中出現以下2項意味著MYSQL根本不能使用索引,效率會受到重大影響。應儘可能對此進行優化。
extra項 |
說明 |
---|---|
Using filesort |
表示MySQL會對結果使用一個外部索引排序,而不是從表裡按索引次序讀到相關內容。可能在記憶體或者磁碟上進行排序。MySQL中無法利用索引完成的排序操作稱為“檔案排序” |
Using temporary |
表示MySQL在對查詢結果排序時使用臨時表。常見於排序order by和分組查詢 group by。 |
優化目標
優化的目標是一定要明確的,不然根本無從下手,針對於前文中提到的sql語句,及explain關鍵字的解釋,我列出了兩條目標:
- 避免全表掃描
- rows引數儘量減小
至於為什麼只列出這兩條目標,主要是因為專案中並沒有複雜的邏輯,也也沒有複雜的查詢,建表時也並沒有根據相關查詢建立索引,而且資料量也不大,因此能夠優化的點並不是太多,即使做了優化也不能顯著的提升速度及效能,因此就先列了兩個簡單的小目標,先體驗一下explain關鍵字在sql優化中的作用。
優化
針對第2條更新文章sql,執行時間較長的原因主要是因為資料量太大,應該是一個朋友在測試的時候做的操作,article_content欄位插入了一條20萬字符大小的資料,因此,主要問題在於插入資料過大,程式碼已經更新了引數檢查功能,在程式中做了限制。
對於另外兩條查詢語句,首先用explain分析sql語句,如下:
注意其中的兩個引數,type都是all,rows較小,都為總記錄,我們的兩個目標是什麼?type不能為all,rows儘量小,這裡似乎滿足了一個條件,其實不然,因為這兩個表的資料量小,因此rows值也小,如果換一張表(book表較大),以相同格式執行一條sql得到如下結果:
rows為416,並沒有因為使用了limit關鍵字而返回較小的值,因此兩條sql都需要做一下簡單的優化。
幾張表都沒有建立索引,是不是就沒有索引了呢?其實不然,你可能忽略了一點,就是主鍵索引,索引的知識點在接下來一篇文章中會寫,這一篇就簡單的提一下,因此優化策略就是使用主鍵索引,將type由all變為index,稍微優化了一點點,改寫後的sql語句如下,分析結果如下:
通過與上面的結果對比,可以看到rows值也變小了。
type由all全部變為index。
總結
由於專案比較簡單,都是操作單表的sql語句,沒有複雜查詢,也沒有多表的連線查詢,速度提升並沒有太多,對於目前的專案來說,不會有特別大的優化動作,如果以後有機會再去結合實際案例去優化,現在就點到為止了,這一篇主要是介紹一下druid監控的成果以及mysql查詢優化的explain關鍵字,因此並沒有做太多的案例及分析,只是做了一些小修改,使得大家對explain關鍵字有了一些瞭解,下一篇會繼續做一些優化改動。