線上故障 | 一條 SQL引發(fā)的血案

事故現(xiàn)場

202-11-19 系統(tǒng)接收到大量的超時告警, 同時業(yè)務(wù)群里面也有很多客戶反饋服務(wù)不可用。

告警圖片-1.png

開始排查

首先上grafana上面查看整體的服務(wù)狀態(tài)艺配,

grafana-cpu-線程.png

從圖中可以看出一點問題來捏鱼,CPU幾乎沒有波動, tomcat線程數(shù)急劇上升, 系統(tǒng)的ops急劇下降唧喉。 這種是比較典型的資源阻塞類問題,為了印證這個想法忍抽,我們再看下當時的系統(tǒng)的GC情況

GC.png

從上面的GC情況下八孝,我們可以看出來,GC還是比較平穩(wěn)的鸠项,整體的停頓市場也不多干跛,平均在100ms以下,雖然不算好祟绊,但是肯定不會造成系統(tǒng)有如此大的停頓

服務(wù)器出現(xiàn)故障排查方法:

服務(wù)器出現(xiàn)故障楼入,先看CPU,如果CPU持續(xù)高漲牧抽,那么肯定是服務(wù)內(nèi)部出現(xiàn)了問題嘉熊,這個時候可以按照網(wǎng)上的常規(guī)解決方法,top -HP pid 查看?耗CPU比較嚴重的線程扬舒,然后導(dǎo)出對應(yīng)的線程棧信息阐肤,就
可以根據(jù)實際的業(yè)務(wù)去分析了, 這種屬于比較直觀的

比較隱晦的資源阻塞問題讲坎,此類問題分為如下兩種:

  1. 比較好排查的孕惜,即使接口慢,比如接口調(diào)用耗費時間久的外部接口晨炕,有大量慢SQL衫画,這些都會間接的導(dǎo)致整體吞吐量下降,最終導(dǎo)致tomcat線程池線程池耗盡
  2. 第二種就更加隱晦了瓮栗,CPU削罩,帶寬,流量费奸,慢SQL鲸郊,內(nèi)存各方面都很正常,但是tomcat線程池直線上升货邓,最終服務(wù)器資源耗盡秆撮。 這種情況我之前有專門寫過一片文章』豢觯《tomcat線程池排查》
    這種情況可以考慮使用jstack命令职辨,導(dǎo)出堆棧信息盗蟆,這里推薦一款工具 gceasy , 可以清晰的分析出線程的狀態(tài)分布舒裤,可以很好的知道線程都堵在什么地方了喳资。

通過上面的已知條件和我們過往的經(jīng)驗,基本上可以判定是有一些接口阻塞導(dǎo)致整體系統(tǒng)處理能力急劇下降腾供。 首先想到的就是慢SQL仆邓。

登陸到阿里云的RDS控制臺上,查看RDS的運行狀況

RDS-CPU.png

果不其然伴鳖,在那個時間段节值,CPU已經(jīng)到了100%了,基本上可以確定是慢查詢的問題榜聂, 在慢查詢的控制臺上搞疗,立馬可以看到當前系統(tǒng)阻塞的SQL。觸目驚心须肆,真的不知道是哪個兔崽子寫的SQL匿乃。

慢查詢SQL.png

罪魁禍首就是這條SQL

SELECT
    o.* 
FROM
    `jm_order` o
    LEFT JOIN jm_order_unregistered_driver d ON o.number = d.orderNumber 
WHERE
    o.valid = 1 
    AND o.state = 1 
    AND o.driver_uid = 0 
    AND o.agents_uid = 0 
    AND d.driverPhone IS NULL 
    AND o.is_push_regular_car = 0 
    AND o.is_lock = 2 
    AND (
        o.from_date > '2020-11-18' 
        OR (
            o.from_date = '2020-11-18' 
            AND o.from_day >= 16 
        )) 
    AND o.type IN (
    11,
    12)

通過explain關(guān)鍵字查詢執(zhí)行計劃

執(zhí)行計劃.png

問題一目了然了,看我紅線框起來的地方豌汇,這個就是問題所在幢炸,我們可以分析下這個SQL,這個SQL

里面有兩張表拒贱,使用了left join 宛徊, 其他的倒是沒什么問題,看索引走向以及掃描函數(shù)柜思,其實看上去都沒啥問題岩调,不應(yīng)該耗時這么久巷燥。

但是看紅色框起來的部分Using where; Using join buffer (Block Nested Loop) , 這句話什么意思赡盘?

下面給大家講一下mysql在表連接的時候使用的算法,同時也讓大家理解一下為什么有小表驅(qū)動大表的說法

mysql表關(guān)聯(lián)算法

Simple Nested Loop算法

這個算法缰揪,屬于簡單嵌套循環(huán)陨享, 說白了就是外層表的結(jié)果作為第一層循環(huán),內(nèi)層表作為第二層循環(huán)钝腺,然后就這樣硬干抛姑,

for (Table t:table) {
  for(Join x:joinTable){
    if(t==x){
      //xxxx ,說明匹配到了數(shù)據(jù)
      for(){
        // 如果有三種表關(guān)聯(lián)的話艳狐。
      }
    }
  }
}

上面這種暴力關(guān)聯(lián)的方法定硝,可想而知效率那是差的一逼,基本上mysql官方也不會使用這種方式的毫目。

simple-關(guān)聯(lián).png

執(zhí)行順序:

  1. 先遍歷table1
  2. 遍歷table得到的結(jié)果蔬啡,逐條遍歷table2
  3. 遍歷完table2之后呢诲侮,繼續(xù)逐條遍歷table3, 返回最終的結(jié)果

執(zhí)行次數(shù)基本上是: table1 * table2 * table3

Block Nested-Loop

這種算法,就是本文中生產(chǎn)環(huán)境實際遇到的箱蟆,mysql默認在沒有建立索引上面使用的算法, 這種做法和簡單嵌套循環(huán)有一點不同沟绪,就是加了 緩存塊 , 減少了循環(huán)次數(shù) 空猜, 將驅(qū)動表的數(shù)據(jù)緩存到 join Buffer里面去绽慈,然后拿Join Buffer 里面的數(shù)據(jù)和內(nèi)層關(guān)聯(lián)表進行匹配,

for (Table t:table) {
  // store t in Join buffer ,
  // 當緩沖池滿了辈毯,執(zhí)行匹配
  if(Join Buffer is full){
    for(Join x:joinTable){
    if(t in Buffer){
      //xxxx 坝疼,說明匹配到了數(shù)據(jù)
      for(){
        // 如果有三種表關(guān)聯(lián)的話。
      }
    }
    clear join buffer // 清空緩存池
  }
  }
  
}

從這里可以看到漓摩,使用了緩存池的話裙士,減少了很多次數(shù),比如:驅(qū)動表100條數(shù)據(jù)管毙,被驅(qū)動表50條數(shù)據(jù)腿椎,那么如果沒有Join Buffer的話,讀表次數(shù):100 * 50夭咬, 加了Join buffer之后啃炸,如果Join buffer的大小可以存儲50條數(shù)據(jù),那么讀表次數(shù)就是: 100/50 * 50 卓舵, 讀表次數(shù)減少了一個數(shù)量級的南用。

需要注意的是,只有在 連表鍵上沒有索引的時候會采用這種方式 掏湾, 也就是本文出現(xiàn)的情況裹虫。

Index Nested-loop

索引嵌套循環(huán),簡稱 INL融击, 說白了就是 連表鍵上有索引筑公,就直接走索引去做嵌套查詢 , 下面我畫一張圖來解釋

索引嵌套.png

驅(qū)動表得到結(jié)果之后尊浪,是直接去索引樹上找對應(yīng)的被驅(qū)動表的記錄匣屡,如果可以使用覆蓋索引的話,那么就不用再做回表了拇涤,這種情況下捣作,效率是相當高的。

得出以上結(jié)果鹅士,立馬給orderNumber 加上索引券躁,走索引嵌套算法就可以了, 系統(tǒng)馬上就恢復(fù)正常了。

看完上面的文字也拜,這下大家明白了為啥有小表驅(qū)動大表的說法嗎旭贬?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市搪泳,隨后出現(xiàn)的幾起案子稀轨,更是在濱河造成了極大的恐慌,老刑警劉巖岸军,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奋刽,死亡現(xiàn)場離奇詭異,居然都是意外死亡艰赞,警方通過查閱死者的電腦和手機佣谐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來方妖,“玉大人狭魂,你說我怎么就攤上這事〉趁伲” “怎么了雌澄?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長杯瞻。 經(jīng)常有香客問我镐牺,道長,這世上最難降的妖魔是什么魁莉? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任睬涧,我火速辦了婚禮,結(jié)果婚禮上旗唁,老公的妹妹穿的比我還像新娘畦浓。我一直安慰自己,他們只是感情好检疫,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布讶请。 她就那樣靜靜地躺著,像睡著了一般电谣。 火紅的嫁衣襯著肌膚如雪秽梅。 梳的紋絲不亂的頭發(fā)上抹蚀,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天剿牺,我揣著相機與錄音,去河邊找鬼环壤。 笑死晒来,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的郑现。 我是一名探鬼主播湃崩,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼荧降,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了攒读?” 一聲冷哼從身側(cè)響起朵诫,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎薄扁,沒想到半個月后剪返,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡邓梅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年脱盲,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片日缨。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡钱反,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出匣距,到底是詐尸還是另有隱情面哥,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布毅待,位于F島的核電站幢竹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏恩静。R本人自食惡果不足惜焕毫,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望驶乾。 院中可真熱鬧邑飒,春花似錦、人聲如沸级乐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽风科。三九已至撒轮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贼穆,已是汗流浹背题山。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留故痊,地道東北人顶瞳。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親慨菱。 傳聞我的和親對象是個殘疾皇子焰络,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容