<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一個 Bug 改了三次,汗流浹背了。。

          共 4385字,需瀏覽 9分鐘

           ·

          2024-07-30 17:00

          Python客棧設為“星標?
          第一時間收到最新資訊

          大家好,我是程序員魚皮。還記得么?幾天前我寫了一篇文章 《一下午連續(xù)故障兩次,誰把我們接口堵死了?!》 ,我從團隊管理的視角給大家分享了我們的編程導航網(wǎng)站線上故障的經(jīng)歷和解決方案。

          雖然這次的故障解決的效率不高,但還是得表揚下我團隊的開發(fā)同學,認真寫了一篇完整的復盤文章。今天,咱們就換一個視角,來看看親身經(jīng)歷和解決故障的開發(fā)同學,他的心路歷程是怎樣的?

          四個字總結(jié):汗流浹背


          昨天是個汗流浹背的周一,我們的后端服務竟然在一天內(nèi)三次不可用,最長的一次達到了四十分鐘!到底發(fā)生了什么?且聽我細說。

          在每周例行的需求評審會后,我就開始對我的需求進行排期,規(guī)劃這周要做的工作,當我開始著手規(guī)劃時,突然發(fā)現(xiàn)線上服務有幾個沒人發(fā)現(xiàn)的后端小 bug,于是我開始了悄無聲息的改 bug。正當我改的上頭時,同事小 y 突然喊我:線上怎么訪問不了了!

          我猛然一驚,我擦,不會是那幾個小 bug 給線上干崩了吧?但是轉(zhuǎn)念一想應該也不是,我趕緊放下手上的工作,開始排查線上服務。

          第一次排查

          定位問題

          1、登錄網(wǎng)站,確認問題

          我趕緊先登錄我們的網(wǎng)站,發(fā)現(xiàn)確實訪問不了。打開網(wǎng)絡控制臺發(fā)現(xiàn)前端的資源響應很快:

          而后端卻一直在 pending,如圖:

          2、登錄容器平臺,查看后端服務狀態(tài)

          由于我們的后端部署在云托管容器平臺,于是我下意識的先去平臺查看服務的狀態(tài),發(fā)現(xiàn)我們服務的平均響應竟然達到了 21 秒!

          然后我合理推測肯定是 qps 猛增,結(jié)果發(fā)現(xiàn),qps 很穩(wěn)定,再看看內(nèi)存、CPU 占用都還算平滑:

          3、登錄接口監(jiān)控平臺定位具體問題

          此時我已經(jīng)發(fā)覺事情不太對,既然 qps、內(nèi)存、CPU 都還算正常,那怎么接口響應這么慢?不過我們后端服務接入了某云服務商的應用實時監(jiān)控服務,我趕緊進入控制臺查看詳細的數(shù)據(jù),一進去就發(fā)現(xiàn)平均每分鐘的響應時間達到了 16.2s:

          此時我的心理:

          但是很快我鎮(zhèn)定下來,我要一點點排查到問題所在,正好這個接口監(jiān)控平臺提供了這些監(jiān)控,我就一個一個點進去看都有啥問題。

          4、查看 jvm 監(jiān)控

          于是我趕緊打開 jvm 監(jiān)控定位下問題,我直呼好家伙,怎么每隔五分鐘 FullGC 一次?因為每次 FullGC 都會暫停應用程序的執(zhí)行,這么頻繁的 FullGC 顯然是有問題的,怪不得線上接口一直無法訪問。

          5、查看線程池監(jiān)控

          但是光看 jvm 監(jiān)控也定位不到問題,我需要趕緊找到 FullGC 的根本原因,于是我點開了線程池監(jiān)控,好家伙,這 TM 所有的線程全都上場了,甚至還有一堆在排隊的。。。

          這是我更不能能理解了,到底是什么阻塞了所有的線程?

          6、 查看數(shù)據(jù)庫連接池監(jiān)控

          帶著疑問,我又打開了數(shù)據(jù)庫連接池監(jiān)控,好家伙,什么鬼?為什么連接池滿了?

          解決問題(bushi)

          看到數(shù)據(jù)庫連接池全部爆滿,我就知道肯定是在查數(shù)據(jù)的時候,所有的請求都在等待連接池空閑,也就導致線程全部阻塞,最終導致頻繁 FullGC,但是也不合理,因為所有的數(shù)據(jù)庫請求按理來說都會自動釋放掉鏈接呀,為什么連接池會滿呢?但是這時候線上事故已經(jīng)發(fā)生很久了,我得先讓用戶能訪問網(wǎng)站再說,要不然用戶還不得罵死我,我趕緊在 Spring Boot 的 application.yml 中配置數(shù)據(jù)庫連接池的最大容量為 20,如下:

          spring:
            hikari:
              maximum-pool-size: 20

          然后發(fā)布到線上,很快線上就可以正常訪問了,使用很絲滑~

          第二次排查

          我本來也以為事情告一段落,可以繼續(xù)修我的 bug 了,順便我還跟同時吐槽了一波,hikaricp 就是不好用,回頭要是有機會一定得換成 druid,還能監(jiān)控 sql、防 sql 注入之類的。。。剛吐槽著,同事小 y 又高呼:線上又卡住了,又不行了,快去看看!

          我心里一沉,好家伙,不給我喘息的機會是吧,我趕緊去看看數(shù)據(jù)庫連接池監(jiān)控:

          怎么回事?20 個連接都不夠用?這不對吧?到底是誰一直占著連接不放手!于是我趕緊進入容器平臺的 webshell,使用 jstack 看看是哪個線程卡死了,發(fā)現(xiàn)都是 TIMED_WAITING,好吧,并不能代表什么,和我第一次排查問題時查看的線程池監(jiān)控的結(jié)果一致。

          暫時沒有找到根本原因,但為了線上能繼續(xù)訪問,我只能先把這些問題拋之腦后,重新發(fā)布一版,把連接池清空掉重新來。

          第三次排查

          這次我老實了,感覺這不是我能處理得了的 bug,趕緊請上我們團隊的技術大佬 yes 哥,我把情況大概給 yes 哥說明了一下:我們線上的連接池總是被很快的耗盡,但是排查不到是哪個地方占用了連接不釋放。yes 哥立馬就反應過來:慢 sql 你排查了嗎?我一驚,好像沒排查。。。

          定位問題

          這次,我直接查看慢 sql,果然有個慢 sql 執(zhí)行了七千多次,而且平均時間居然達到了 1.4 秒,這連接池根本沒有閑著的時候啊!

          分析問題

          再仔細看下這個 sql,發(fā)現(xiàn) scene 這個字段沒有加索引,也就是說每次這個請求都會走一遍全表掃描,然后我看了下這個 sql 執(zhí)行的場景,是在微信公眾號掃碼登錄時,前端輪詢用戶是否已經(jīng)掃碼并關注公眾號,如果掃碼關注了公眾號則登錄成功。大致的流程如下:

          那么很顯然,我們的問題就出在后端根據(jù)場景碼輪詢用戶信息這里,這里有個慢 sql,因為我們的 scene 沒有設置索引,因此導致每次查詢用戶是否掃碼登錄時都要進行全表掃描,用 explain 看下這個 sql:

          解決問題

          但是當時我比較猶豫,因為加上這個字段后,大概率只會命中一次,只要有一次查到了,這個 scene 就無效了,所以我總感覺這個索引加了會浪費性能。我跟 yes 哥表達了我自己的想法,但是 yes 哥的想法是,就算他只有一次生效,至少不會在輪詢的時候一直掃全表,畢竟這個 sql 是前端輪詢的產(chǎn)物。

          我想了想有道理,這時候,我們老板出來了(也就是魚皮):怎么回事,你們在討論什么?!線上服務不可用都已經(jīng)四十多分鐘了,還不先把服務恢復了再討論詳細解決方案?

          于是我趕緊按照 yes 哥的方案,把這個索引加上了,讓我沒想到的是,效果立竿見影,線上一下就可用了,看下連接池的變化:

          此時我長舒了一口氣,終于算是搞定了,這時候我再 explain 一下,發(fā)現(xiàn)這時候已經(jīng)是走索引了:

          好吧,我為我的無知向 hikaricp 道歉,不是你的鍋,是我的鍋~

          后來我又查了下阿里巴巴的 Java 開發(fā)手冊,發(fā)現(xiàn)這本手冊第五章中的第(二)節(jié)里明確寫了創(chuàng)建索引時要避免的誤解,如下圖:

          我就是因為第二點認為索引不應該隨便創(chuàng)建,因為可能會導致拖慢記錄的更新之類的,后來想了下,其實 user 表的更新并不頻繁,包括這個 scene 的更新也不頻繁,因為用戶在正常使用過程中又有幾次會重新觸發(fā)登錄呢?

          好了,汗流浹背的周一就這么度過了,算是長了個記性,趕緊把所有的慢 sql 都看一下,能加索引加加索引,不能加索引的,看看能不能換個實現(xiàn)方式。以后排查問題又多了個思路,線上的連接池用完了要先排查下有沒有慢 sql 導致鏈接一直沒被釋放,再往下排查。

          魚皮總結(jié)

          通過這次事故,暴露了我們開發(fā)同學經(jīng)驗的不足。

          1. 遇到服務卡死問題時,趕緊先擴個容新增一臺可用實例,然后再對著原有故障實例的現(xiàn)場進行排查。
          2. 時間緊迫的情況下加大數(shù)據(jù)庫連接數(shù)沒問題,但是才加了 10,顯然是有點太保守了,我們的數(shù)據(jù)庫還是扛得住的。
          3. 應該能夠預料到問題并沒有根本解決,并且趕緊繼續(xù)觀察和排查,怎么就開始做別的了呢?
          4. 排查定位問題的效率不高,像 “如何定位線程池爆滿問題” 這種八股文知識還是要背背的。

          而且我在 上一篇文章 中也提到了,導致本次故障的慢 SQL 我們早就發(fā)現(xiàn)并且發(fā)到群里了,結(jié)果團隊幾個開發(fā)竟然沒一個人去處理。。。

          當然,最遺憾的是,這篇寫出來的事故復盤仍然是殘缺的!

          現(xiàn)在的這個解決方案并不完整,在我看來還是 臨時 解決了這個問題。信不信,并發(fā)量再大點,系統(tǒng)的某個地方還會出現(xiàn)類似的問題?難道到時候再汗流浹背一次?俗話說事不過三,這次的損失我們忽略不計,下一次可就不一定了。

          既然我們用了可以根據(jù)資源利用率自動擴容的容器平臺,那么當并發(fā)量增大、單個節(jié)點處理不過來時,就應該自動擴容,自然能夠應對更大的并發(fā)請求。通過本次事故,我們發(fā)現(xiàn)請求連接數(shù)滿的時候,節(jié)點的 CPU 利用率才不到 20%、內(nèi)存才不到 60%,根本達不到擴容的閾值。所以應該適度增大數(shù)據(jù)庫連接池數(shù)量、增大服務器請求處理線程的數(shù)量,提高系統(tǒng)資源利用率,并且通過壓力測試來驗證能否觸發(fā)自動擴容。或者調(diào)整容器的擴容策略,也是一種方案。

          最后,希望普天下的程序員寫代碼都不遇 bug。

          往期回顧

          1、Linux Mint 22“Wilma”正式發(fā)布
          2、不到2MB,很炸裂的神器!
          3、8年前就免費的功能還在扣費!運營商的回應令人無語
          4、程序員應該掌握的三種編程語言——有Zig無Rust?
          5、學了十幾種編程語言后,我終于悟了!
                


          點擊關注公眾號,閱讀更多精彩內(nèi)容

          瀏覽 153
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  操逼高清无码 | 欧美xxx一区二区三区 | 黄色免费在线观看 | 超碰在线97啪啪啪 | 日韩性爱视频在线观看 |