慢查詢業務場景:
原先在我們系統中要統計一些人員的單位 部門信息的數據情況,比如總的男女人數,每個單位下的男女人數等等,然后原來的sql是這樣寫的? 根據一個單位的id 然后對一張表做出多個子查詢進行查詢,這時候統計記錄 由于加載過的數據在mysql緩沖池里面,然后多個子查詢也不是很慢,大概是幾百毫秒就能統計出數據。
? ? ? 但是甲方在這個查詢的基礎上加了一個新需求,因為一個人員可能轉部門,他需要把轉過部門的人員恢復到原來部門進行一次重新的統計
? ? ? ?這時候面對這種新需求,我們應該怎么做,剛開始我想的時候直接修改原來的sql,然后在前端加一個是否統計之前的還是之后的一個flag標志,進行查詢,但是原來的sql寫到了xml里面,一個sql大概有400行,他們統計調用了好幾個接口,加起來修改的sql要有1800行,實在是工作量有點大,覺得這樣不可行,然后我又想到 直接復制一份表出來,然后人員部門數據用原來的部門,但是這樣業務也有侵入,因為如果這時候人員添加 ,人員轉部門我都要在響應的接口上修改,這樣修改的太多了,于是這兩種方法都作廢
?最終我想到用一個視圖,寫一個查詢,根據原來的人員的單位信息表和轉單位表做一個連接,用
COALESCE()
函數 直接使用轉單位前的數據進行統計,然后重新寫一個xml文件,對照著之前的修改一下接口,這樣能最少程度上達成任務,減少工作量,而且最少程度侵入業務
這時候我寫完之后,自己本機測試沒什么問題,大概2秒左右就能出數據,但是當上線到服務器上,測試發現該次查詢居然足足用了17秒 如圖
這怎么能行!
優化思路
于是我在想改變原來別人寫的屎山sql費時又耗力,只能從自己寫的視圖入手,我先手動在服務器上查詢了自己的創建視圖的sql,發現用了2.3秒,然后sql又根據視圖依次查詢好幾次,估計疊加起來才那么耗時,于是explan自己的sql語句 發現
這個type是最差的all,過濾的數據只是一百多條,可見性能非常差了,
于是理所當然想到加快查詢? 那么我們就進行加索引,但是我們怎么加如何加是一個問題
首先我們需要在滿足最左前綴法則的基礎上,然后不觸到索引失效的場景,然后進行加索引
我的視圖后面的sql是這樣的
所以需要在兩張表上加索引,因為我們的shzt 和scbz 都是常量,在sql執行的時候,mysql內部自動優化會先篩選過濾常量的數據,于是我們需要對zzy這張表的shzt 和scbz字段先加索引,然后再xsid和yxpcid 加的索引放到后面,組成一個聯合索引, 對于xs表? scbz索引需要放到前面,xsid和yxpcid放到后面就可以了,這樣會最大化的利用效率
加完索引我們再explain分析一下發現
type已經變成了ref 也是比較快了,而且都用到了索引。
其中為什么 在xs表中 只用到const 常量索引,是因為 在左連接中xs表的xsid和yxpcid只作為關聯條件而不是作為過濾條件,在zzy中這兩個字段是過濾條件根據zzy中的兩個字段匹配xs中的字段,所以在zzy中能用到
因此索引字段的順序必須與?“過濾優先級”?一致:過濾性強(能排除大部分數據)的字段放前面,關聯字段放后面
?那么有的就會穩了 為什么不把 常量索引放到后面,把字段索引放到前面呢?因為sql執行的時候會優先過濾常量,把常量索引放到前面使用索引的效率更高
無法優先通過過濾條件縮小范圍
索引會先匹配最左側的?XSID
?和?YXPCID
,但這兩個字段的值來自?xs
?表的關聯(即?zzy.XSID
?要等于?xs.XSID
,而?xs.XSID
?是動態的,取決于?xs
?表的查詢結果)。
此時,數據庫無法先通過?SHZT=1
?和?C_SCBZ=0
?過濾掉大部分無效記錄,只能先根據?xs
?表的?XSID
?和?YXPCID
?去?zzy
?表的索引中找匹配的記錄,再在這些記錄中過濾?SHZT=1
?和?C_SCBZ=0
。
這相當于 “先找關聯,再過濾”,而過濾條件本可以更早排除大量數據,導致索引掃描范圍變大。極端情況可能索引失效
如果?zzy
?表中?XSID
?和?YXPCID
?的重復值很多(比如大部分記錄的?XSID
?都相同),數據庫可能認為 “先通過索引找?XSID
?再過濾”,不如直接全表掃描后過濾更高效,此時索引會被放棄(type
?變為?ALL
)
假設?zzy
?表有 10000 條記錄:
- 其中?
SHZT=1
?且?C_SCBZ=0
?的記錄只有 1000 條(有效數據)。 - 這 1000 條中,與?
xs
?表關聯的?XSID
、YXPCID
?只有 100 條。
原索引?(SHZT, C_SCBZ, XSID, YXPCID)
:
- 先通過?
SHZT=1
?和?C_SCBZ=0
?從索引中定位到 1000 條有效記錄。 - 再在這 1000 條中,通過?
XSID
、YXPCID
?關聯?xs
?表,找到 100 條匹配記錄。
總掃描:1000 條(高效)。
顛倒后索引?(XSID, YXPCID, SHZT, C_SCBZ)
:
- 先根據?
xs
?表的?XSID
、YXPCID
?去索引中找所有匹配的記錄(假設有 5000 條,因為很多?XSID
?對應的記錄?SHZT
?可能≠1)。 - 再在這 5000 條中過濾?
SHZT=1
?和?C_SCBZ=0
,最終得到 100 條。
總掃描:5000 條(低效,比原索引多掃 4000 條)
因此我們需要這樣設計索引才更高效
最后的優化查詢時間為
后續測試該接口基本穩定在650到700ms