高併發系統之大忌-慢查詢

最近又遇到了一次慢查把db(mariadb10)幾乎打掛的案例,做爲一個核心支付系統的技術負責人,真是每日如履薄冰。由於以前支付系統常常出問題,如今各個BG對支付系統都盯得很緊。此次要不是我及時讓DB給暴力清理數據,沒準又提一個P2故障;mysql

抱怨歸抱怨,過後覆盤,一絲都不能馬虎。首先,描述一下故障的全過程。原由是咱們支付系統有一個異步隊列,這個隊列使用的一張mysql表存儲,異步回調業務線的任務(姑且表名稱叫task),都會首先放這裏。同時這個task表還有其餘的異步任務,不一樣的任務使用task_type字段來進行區分。而後應用系統有一個定時任務,掃描這張表是否有待消費的任務,若是有,則會取出來進行消費;典型的生產者消費者模型;sql

 

這裏的task說的再具體一點:數據庫

一、全部的異步任務都在這張表,有支付成功通知業務線消息,有給結算系統推送支付信息的任務;架構

二、消費者在任務處理成功後,則會把任務從task表刪除。因此這張表常常是空的;併發

消費者根據不一樣的任務,調用不一樣的上游訂單系統和結算系統。出故障時,是由於推送支付信息的結算系統接口超時,出了問題,致使任務被積壓到了task表。異步

 

 

任務積壓以後,消費者線程池很快就被積壓的任務佔滿,致使應該通知BG訂單支付成功的任務也被block住,進而影響到訂單支付成功率。線程

當時我已經意識到,咱們的消費者線程池隔離沒有作到位,馬上找DBA將推送給結算系統的任務進行了備份並清理。而且囑咐DBA定時清理推送結算任務的數據。這樣才化解支付成功率繼續下滑的趨勢。3d

危機解除後,咱們和DBA配合進一步排查問題,找出了事情的根本緣由。日誌

原來是推送結算信息的邏輯中,有一個對task表的查詢,而這個查詢的sql,沒有建索引。這樣當這類任務數量積壓的比較多時,查詢會愈來愈慢,慢查致使mysql堵塞。堵塞致使消費者沒法拉取任務,進而影響到其餘通知BG的任務的消費;咱們分析了一下日誌,其實咱們的程序查詢數量當時3分鐘大概查詢了1萬屢次,能夠說qps很少。可是問題出如今sql沒法命中索引,把mysql的worker thread都用完了。給咱們研發的感受,mysql是如此的脆弱,2w多條數據,查詢沒有索引,幾千個select,就能把它打掛。blog

幾乎相似的案例,一年前,咱們也碰到過一次。當時支付系統有一個bug,用戶每支付一次,都會把支付客史中一個月以前的數據都清理一下(1月1日,清12月1日以前的數據,2月1清理1月1日以前的數據)。這個bug藏的很深,這塊代碼也不多有業務需求,一直沒有被發現。可是,是雷就會有爆炸的一天。3月1日凌晨,支付系統忽然全部接口都掛了。DBA最終定位是刪除支付客史的sql。這個問題,咱們研發一開始是不認可的,畢竟這個sql,在線上跑了2年多,一直沒有出過問題。DBA說這個delete語句刪3000w數據,並且在不斷的請求,數據庫固然扛不住,咱們反駁說,這個客史表一共才3000w數據。過後咱們發現,DBA的描述有誤,不是說刪除3000w,而是這個delete語句沒有走索引,每次要掃描3000w數據。這樣才能解釋通,也就是說,這個delete進行了全表掃描。而實際上,這個delete是按照時間刪除的,而且時間字段是有單列索引的,可是爲何這個delete沒有走索引呢?咱們最後猜想,多是由於2月份天數太少致使。之前,可能數據比較少,每次刪一天,或者2天的數據,mysql可能會走索引。可是3月1日比較特殊,因2月28日刪的是1月28日一天的數據,3月1日卻要刪除1月29,30,31三天的數據,mysql可能認爲刪除這麼多數據,沒有必要走索引了。

 

遇到相似問題如何解決?

一、讀寫分離。

二、提早消滅慢查詢;

三、對異步任務作好線程隔離;

 

關於mysql的線程池,我最近也瞭解了一下,收穫也不小,給你們推薦一篇好文章;

https://www.jianshu.com/p/88e606eca2a5

 

關注個人公衆號「猿界汪汪隊」,關注大併發架構實戰。

相關文章
相關標籤/搜索