慢查詢SQL優化

      筆者現在在一家廣州大型電商公司上班,最近公司的業務上發生了一件很嚴重的事情,用戶在平臺上操作了退貨後,卻沒有快遞員上門攬貨,快遞公司也沒有收到相關請求,經過詳細排查,發現是慢查詢SQL導致的,這個問題影響了1000多單,給用戶帶來了極差的用戶體驗,也讓我們部門上了公司黑榜,導致整個技術部門收穫了一個三星故障,如果一個年度內部門收穫兩個四星故障,那整個部門的年終獎就沒了,三星是比四星還要嚴重的故障。雖然不是我們小組負責的項目,但是屬於同個三級部門,屬於隔壁小組搞出來的,整個技術部門對慢查詢SQL進行大量排查,其實這個問題一直是有告警出來的,只是技術人員一直無視,才導致瞭如此嚴重的事故。

    用戶退貨的流程如下:

用戶---》操作退貨請求--》系統收到退貨請求--》系統向快遞公司下達攬收請求--》快遞員上門攬貨

監控中心和DBA把我們小組負責的系統的慢查詢SQL全部整理出來,查詢時間超過0.1秒的都被認定是慢查詢SQL,全部有150條左右,領導安排我來做慢查詢SQL的優化,這些慢查詢SQL的優化大體可以劃分成以下幾類:

可以使用explain查詢msyql的執行計劃。

1.添加索引

場景:我們有個日誌表,這個日誌表有兩個核心字段,包括result_code和create_time,result_code用來記錄業務的操作結果,如果業務執行成功,則result_code=200,佔大多數;如果業務執行失敗,則result_code=400或其他,佔少部分,create_time是記錄創建的時間,這個表每天新增的數據量是幾百萬,create_time是有加索引的,但result_code字段沒加索引,導致要查出某段時間業務執行失敗的數據時,需要掃描的數據量非常大,從而導致慢查詢SQL。

解決辦法:給字段result_code加索引。

這裏有個前提,那就是result_code=200佔多數,result_code =400佔小數,所以對字段result_code加索引後,查詢result_code=400的性能會提高很多,但如果result_code=200和400的數量相關不大的話,其實也不會有太大的提升。

系統中相當一部分慢查詢SQL都是可以添加索引的方式進行優化。

2.改變索引字段

比如有以下SQL:

select count(*) from log where create_time>DATE_SUB(NOW(), INTERVAL 1 DAY) 

log中有核心字段result_code和create_time,其中建了聯合索引index(result_code,create_time)

這條SQL不會中索引,通過優化添加查詢條件的方式命中聯合索引,SQL改成

select count(*) from log where result_code IN(200,400) and create_time>DATE_SUB(NOW(), INTERVAL 1 DAY) 

3.停掉無用的SQL

有些業務告警配置了查詢時間段比較長,比如15天,或者字段使用模糊查詢條件like '%XXX%',導致掃描數量量較大,向相關人員確認沒該需求後就關閉告警,停掉SQL。

4.添加查詢條件

場景:很多頁面都有一個查詢功能,頁面上有很多查詢條件中以填寫,初次進頁面時,如果沒有填寫一些查詢條件或者查詢條件中的過濾效果不好,就很容易產生慢查詢SQL。

比如頁面的初次查詢是:

select count(*) from log

如果表log中的數據量達到幾千萬或者上億,那麼這個查詢花費的時間少則幾秒,多則十幾秒。

解決辦法:設置默認的查詢字段,比如默認查詢一天內的數據

select count(*) from log where create_time>DATE_SUB(NOW(), INTERVAL 1 DAY) 

5.程序優化

場景:很多SQL使用模糊查詢 like '%XXX%',這種查詢是不會中索引的,雖然MySQL5.6.24上InnoDB引擎也支持全文索引,但如果表的數據量比較大的話,全文索引會佔用很大的空間。

解決辦法:在某些特定場景下,可以在程序中把關鍵詞識別出來,放入單獨的字段,並加上索引。

這只是在特定場景纔可以生效,而且要修改程序,比較費時。

6.改變引擎優化方向

場景:同時使用id和create_time索引,比如以下SQL

select count(*) from log where create_time>DATE_SUB(NOW(), INTERVAL 1 DAY)  and id>0

我們有個定時器是掃描出最近幾天執行失敗的數據,拿出來進行重試,使用id和創建時間進行過濾,初次循環使用maxId=0,下次循環maxId=maxId+count,但第一次循環時由於是maxId=0,所以就是上面的SQL語句

MYSQL引擎會認爲走ID主鍵索引是最優的,相當於掃描出全表的ID,再使用create_time索引進行過濾,導致查詢效率極差,需要300多秒。

優化的辦法是,首次查詢時取消id>0的條件,第二次查詢時才加上id>maxId,這樣的首次查詢時間就降到2秒多。

7.無法優化。

SQL中的查詢字段有中索引,但需要掃描的數據量較大,或者由於使用like '%XX%'導致全表掃描等等,很多這類場景並沒有多少可以優化的空間。

8.幽靈事件

遇到有個SQL,大多數情況下是有中索引,花費0.4秒,但有時候是全表掃描,花費4000多秒,我百思不得其解。

SQL的大概結構如下:

select count(*)as ct from business t 
inner join order_log ol on ol.no = t.no and ol.type =  'a' 
inner join order o on o.no = ol.no and o.type = 'a'
where t.create_time >= DATE_SUB(NOW(), INTERVAL 2 DAY) 
and t.create_time < DATE_SUB(NOW(), INTERVAL 1 DAY) 
and t.message_body like '%a%' and t.message_body not like '%b%' 
and t.result_code = 400

9.MYSQL內存不足

有些SQL,有些情況下查詢時間只花了幾十毫秒,但有時候花費了兩三秒,SQL是有中索引的,DBA回覆說是系統的內存不夠導致需要將掃描出來的數據放入磁盤,從而使用查詢效率低下。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章