sql執行計划與優化


  在我們實際工作中大部分人會遇到sql優化的問題,這篇文章主要介紹SQL優化相關。首先我們怎么發現我們的sql執行效率低呢,最簡單的方法就是當用戶反饋慢的時候我們就會知道哪里可能會有sql效率影響的問題,這里排除其他影響情況,只考慮數據庫sql慢的問題。當然這種方式對於我們來說很被動,我們還可以通過什么方式找到有性能問題sql,我們可以通過MySQL的配置文件來開啟慢查詢日志,我們可以設置slow_query_log=on,因為MySQL默認是不開啟,如果MySQL是運行狀態的MySQL可以使用set global命令來啟動,我們還可以指定日志文件路徑slow_query_log_file,如果不設置默認在MySQL數據目錄中,除了文件路徑,我們還可以設置記錄sql執行時間的伐值long_query_time,可以精確的毫秒級別,但是他的單位是秒,如果不設置默認為10s,log_queries_not_using_indexes配置會記錄未使用索引的sql,因為我們知道索引的建立會占用一定的磁盤空間,對添加修改刪除也會有影響,如果一個索引的建立一直未被使用,我們可以對其進行分析,是重復索引了還是冗余了等問題,關於MySQL的配置文件的詳細配置可以自行查閱資料,MySQL配置位置文件的位置在linux服務器我也就不多去介紹了,對於使用docker鏡像安裝的MySQL可以指定外掛我們自己的配置文件。MySQL的慢查詢日志可能會記錄很多,我們肉眼去看可能不是特別現實,我們可以借助慢查詢日志分析工具,例如MySQL官方推薦的mysqldumpslow,在MySQL服務器自帶mysqldumpslow工具,可以直接使用。我們除了可以通過日志記錄的方式查看也可以通過語句實時查詢有幸能問題的sql。

  

  

  我們可以獲取到進程id,用戶,ip,使用的數據庫,語句,時間等。我們在進行sql分析前先來了解一下怎么查看sql的執行計划,執行計划的每個參數又是什么意思呢?我們繼續向下看。

  什么是sql的執行計划,他就像一份體檢報告,這份報告告訴你沒想參數的結果。我們通過EXPLAIN來查看我們的sql執行的情況,貼個圖

  

  

  這是一個很簡單的sql兩個表關聯查詢通過where子查詢作為條件,從第一個列來看,有1跟2,再看sql我們能看出來MySQL首先執行的應該是where條件里的子查詢,通過id這列我們能看出來MySQL執行時id越大執行優先級越高,如果id相同則順序執行。

  第二列select_type查詢類型。查詢類型都有:

  SIMPLE:簡單查詢,查詢里沒有子查詢或者union

  PRIMARY:查詢包含嵌套查詢,最外層查詢的類型為PRIMARY

  SUBQUERY:在SELECT或WHERE列表中包含了子查詢,子查詢的類型為SUBQUERY

  DERIVED(衍生):在FROM列表中的子查詢的類型為DERIVED,MySQL會將查詢的結果放在臨時表中,我們上面寫到的sql沒有產生這個類型只需要將store寫成子查詢即可,我們就不做演示,我們的查詢有的時候需要臨時表他會加快我們的查詢效率,但是臨時表的數據量很大,它也會影響我們的查詢效率,對於臨時表我們后面會提到

  UNION:若第二個SELECT出現在UNION之后,則被標記為UNION;若UNION包含在  FROM子句的子查詢中,外層SELECT將被標記為:DERIVED

  UNION RESULT:從UNION表獲取結果的SELECT的類型為UNION RESULT

  UNION跟UNION RESULT光看理論可能看不懂,舉個例子

  

  

  這樣一看就明白了,出現在union后面的為union類型,而兩個表合並的為union result類型

  第三列看名字就知道他的意思是表

  第四列表的分區(看下圖),對於分區這里暫不介紹

  第五列訪問類型,常見的從低至高all,index,range,ref,eq_ref,const,system。當然system表里只有一行記錄,實際業務表基本不會出現,可以忽略。

  const:通常通過主鍵索引或者唯一索引進行查詢,因為只需要匹配一行,所以很快。

  

  

  eq_ref:通過主鍵或者唯一索引相等作為條件,對於每個索引只有唯一匹配,舉個例子:

  

  ref:查詢的索引列查詢出來的記錄不唯一,沒有唯一匹配,舉個例子:

  

  他跟eq_ref區別就在於一個是完全匹配,一個是查詢的結果不唯一,所以前者更多用於主鍵索引或者唯一索引

  range:范圍索引,通過'>','<',in,between,like

   

  

  

  

  

  注意我們上篇文章提到不是所有的like查詢都會走索引

  index:全索引掃描,我們知道索引的大小遠小於數據的大小,即使是index他的查詢效率也比全表掃描高很多

  

  查詢出來的列值在索引中不需要再去找到對應行的數據,也叫覆蓋索引

  all:全表掃描,這個很簡單就不寫例子了

  第六列possible_keys可能用到的索引,當查詢的列,多個索引列包含,會列出可能執行的索引

  第七列key真正執行的索引如果該列為空可能是沒有索引或者索引失效

  第八列key_len索引的最大可能長度,越大代表查詢用到的索引更多,但並非越大越好,在同樣查詢結果的情況下key_len越小越好 

   

  

   第九列ref查詢中使用索引被用到的值或常量

   第十列查詢需要掃描的行,這惡值越小越好

  第十一列代表查詢的結果占掃描列的百分比,上圖都是100,這里不做介紹

  第十二列額外重要的信息,先把比較重要的放到前面說

  Using index代表我們查詢用到了索引

  Using temporary代表我們的查詢通過臨時表存儲,相當於創建一張表然后將數據復制進去比較出結果后郵件臨時表刪除,這樣的索引使也會嚴重影響查詢效率,最常見用於分組或者排序,舉個例子,創建的索引如下

  

 

  

  

  

  

  

  

  

  

  

  

  

  還能舉出例子的情況還有很多種,這里就不一一列舉了,通過幾個執行計划能看出來在我們查詢時通過復合索引進行了分組的時候,當訪問類型為ref級別的時候只有在復合索引跳過某個字段的時候會出現,在訪問類型為range級別時,group by后面的字段必須按照符合索引的順序,后面的可以缺失,第一個不能缺失,store_name,store_user_name,status|store_name,store_user_name|store_name不會出現Using temporary,store_user_name,status等都會出現Using temporary,因為range為范圍查找,范圍查找后的所有條件都失效,所以沒有使用后面的索引。導致出現Using temporary

  Using filesort代表MySQL無法通過索引進行排序,通過文件排序的的方式進行排序

  

  

  

  

  

  

 

  

  

   幾個例子發現它的出發條件跟上面的觸發條件一樣,同樣排序沒有通過索引的方式排序通過文件排序也是會影響查詢效率的

  Using where表示使用了where條件

  Using join buffer使用了連接緩存

  impossible where表示where的條件總是查詢不出結果,我們的狀態啟用或者禁用,不可能一個店又是啟用又是禁用

  

  說了這么多,舉幾個例子來看一下具體優化:

  1.單表查詢:

  select * from store where store_name='測試' and store_user_name LIKE'測試%' GROUP BY `status`,我們還是用這條sql來看,前面大致講了一下原因,但是沒有說這條sql應該怎么改,我們還是貼出執行計划

  

  創建的索引列也貼出來,這樣就不用去上面找了

  

  這條sql能看出來使用了索引,訪問類型是range,也就是說范圍查詢后面的列是不走索引的,按我們有什么辦法讓他走索引呢,我們上面把like查詢換成了等值查詢之后就走了索引,但是這樣做並不滿足我們的優化需求,為了在滿足業務正確性的前提下我們可以優化索引

  我們將復合索引中需要范圍查詢的索引去掉

  

  然后看一下執行計划

  

  發現訪問類型為ref,從這里就能看出來我們的優化有了效果,並且我們也解決了Using temproary跟Using filesort,key_len的長度也減少了,前面也提到在同樣的查詢結果下key_len越小越好

  2.兩表聯查:

  select * from store s LEFT JOIN user_account ua on s.store_mobile=ua.mobile,我們將兩個表的索引都刪掉,執行

  

   毫無疑問兩個都是全表掃面,那么問題來了,我們加索引應該怎么加,給哪個表加,我們先試一下從第一個表開始

  

  

  發現加了索引之后訪問類型沒有改變但是我們掃描的列變少了,這可能是MySQL查詢優化器覺得全表掃描更優,然后我們將store表索引刪除,在user_account表上加索引

  

  我們能看出來我們加索引的表應該是右邊的表,right on一樣我們就不做演示了,我們反着加,三個表聯查我們也是在后面兩個表加,我們在加索引的過程一定要注意索引在什么情況下會失效,上篇文章我們提到過,解決索引失效也是我們優化sql的一部分,在滿足業務的情況下當然我們也可以用小表驅動大表,還有一個特殊的請款列出來

  

  order by遵循最左匹配原則,但是這種情況例外,將會產生Using filesort。

  本篇文章提到的優化方案不一定是最優的,可能也有其他多種方案,文章可能也會有漏洞,歡迎大佬吐槽。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM