項目中常見BUG的排查方法彙總

1 前言
最近各項目組頻繁出現技術問題,問題主要包括:測試不達標、生產事故兩大類。常常有人問我尋求技術支持,其中包括排查辦法、思路、有事直接參與技術排查過程中。怎奈我一人能力有限,無法滿足整個DB部門的需求,痛定思痛,決定總結此文,以紀念過去十餘年來踩過的坑。
2 需要的技術知識
計算機技術是一門系統的科學,任何一個點上出現的問題都有可能導致全局性的問題,我們有時需要從計算機硬件、網絡硬件、數據庫、各種中間件技術及工作原理、算法、編程語言技術特點、業務需求、架構設計等各個方面進行綜合分析才能判斷和定位問題。以上知識的學習過程有可能很漫長,不是一個人力所能及,所以排查複雜問題的過程需要各方面的專家綜合會診纔可以解決。所以這也是個團隊合作的過程。
3 需要的材料
問題排查的過程就像福爾摩斯探案一樣,不能放過任何蛛絲馬跡,並作科學的分析與論證才能定位問題的本源。雖然並不是我們面對的所有問題都很複雜,但是當我們有更多的信息時有助於我們更加快速準確的定位問題。更具上面我們提到的知識來說,我們定義複雜問題所需的資料一般包括但不僅限於:系統設計文檔、業務流程、應用和各種組件的日誌、Dump信息、硬件配置單、網絡部署圖等內容以及系統問題現象描述。
4 觀察現象:
系統當前展現出的問題現象往往是我們確定系統問題的第一手資料。這些感官現象包括:系統慢、宕機、夯死等現象,同時監控系統也會伴隨出現警告或者錯誤信息提示。但是這些信息僅僅是出於感觀方面的,不能定位和發現問題起到指導作用,爲了發現問題我們需要從以下幾方面進一步觀察細節。
4.1 硬件類:
事實上大部分的故障排除過程中,硬件問題往往具備以下幾個特點:
**1、**最容易發現的;通過幾個命令即可快速查看狀態,並發現和定位問題;除非他是被間接影響的。
**2、**最容易被忽略的;往往由於硬件原理理解不夠,或者是開發者的慣性思維將問題優先假設在代碼層面上。
硬件類問題主要觀察的指標包括CPU使用率、物理內存佔用率、SWAP佔用率、網絡IO佔用率、磁盤IO佔用率等等;比較好用且全面的檢查工具是nmon,如果沒有安裝的情況下可以使用其他命令進行綜合排查。
一般情況下硬件類指標的影響都是伴隨型出現的,但是我們通過單個特徵的特點,進行綜合性分析。一般情況下單純的CPU佔用率高主要是由於算法複雜,運算量大,邏輯判斷多等情況導致;物理內存佔用率高,如果是對於java程序由於其xmx參數的特點,可以不用過度關心,此時主要看SWAP內存佔用率情況,如果這個值很高的話會導致系統運行緩慢;網絡IO佔用率高,分爲讀/寫兩種,這主要是數據交換頻繁導致,;磁盤IO佔用率分爲讀/寫兩種,要區分是哪個值高,然後通過程序設計的特點排查和定位問題。
4.1.1 虛擬機參數:
大部分程序員第一感覺是虛擬機跟物理機一樣,但實際上受到虛擬化技術原理的限制,會有一些我們意想不到的問題。通常我們見到的硬件類常見問題以外,虛擬機環境也同時受到了所在物理機各種參數的限制,而出現各種各樣的問題;最常見的問題就是一臺物理機上部署了多個虛擬機,此時在高併發情況下,物理機出現整體的性能瓶頸。
在使用虛擬機遇到性能瓶頸時,建議結合虛擬機所在的物理機的磁盤io以及網絡io配合進行檢查。因爲一臺物理機上往往部署多個虛擬機,對於有條件的系統建議將一些網絡帶寬和磁盤io佔用較大的虛擬機儘量隨機部署在硬件設備上,防止個別物理機資源過載。
4.2 網絡設備及負載均衡情況:
一般情況下我們用到的網絡設備和負載均衡設備不會出現問題,除非出現了網絡限流;負載均衡設備使用了不合理的參數配置也會導致問題,如session的綁定,以及負責均衡的算法規則上。
網絡設備大部分都具備抓包功能,如果遇到網絡問題,可以配合抓包工具幫助定位問題。
4.3 JVM類:
4.1.2 JVM內存:
由於JVM的版本和廠商不同,默認採用的垃圾回收器算法也不盡相同,而且的JVM內存管理機制可以更換,因此這裏不過多加以論述,但是對於問題排查方式大致相同。JVM內存常見問題主要有兩種,一是由於內存的堆佔用過大多導致的Full GC次數過於頻繁,表現是程序吞吐量降低,性能逐漸下降。二是JVM內存溢出,表現是直接拋出內存溢出的異常。這兩種現象的出現實際上是伴隨出現的,一般情況下Full GC會先出現,之後轉爲內存溢出。主要是看內存泄漏的速度,如果速度較快full GC的過程就會很短,如果速度慢,則會表現出很長一段時間的性能低下。檢查辦法有兩個:
1、oracle jvm一般使用jmap查看jvm堆當前狀態,統計被實例化數做多的類和佔用空間最多的類型。
2、通過使用GC日誌,查看Full GC次數與頻率
4.1.3 Jvm 線程狀態:
Java線程狀態包括一下幾種:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。一般情況下具備潛在風險可能的主要是BLOCKED和WAITING,主要表現爲系統吞吐量低,運行緩慢。可以使用jstack查看線程當前運行狀態。
4.4 數據庫類:
數據庫出現問題一般都是比較嚴重的技術問題,這種情況下會出現大面積的應用不可用或者服務緩慢,建議讓DBA配合進行排查。除此之外以下數據庫方面的檢查可能會幫助定位到問題的原因:
1、 查看數據庫的活動的session數量,及持續時間,可以統計相關的SQL執行時間
2、 查看數據庫索引狀態是否失效的
3、 查看數據庫CPU是否高,說明有大運算量的SQL在執行,這種運算量可能是SQL邏輯複雜、全表掃描、沒走索引、數據量大等情況時出現
4、 使用SQL執行計劃查看SQL的執行成本,一般是走了不該走的索引,或者是沒走索引導致
5、 檢查數據庫中是否有大量的鎖
6、 檢查相關sql對應的表中的數據量是否過大
4.5 消息隊列:
消息隊列除了出現明顯的服務宕機情況以外,大部分情況下的表現都是消息積壓。我們可以通過觀察一下現象協助排查問題
1、 查看消息積壓情況,是否積壓是持續增長的,用於判定情況是否會有好轉的可能。
2、 檢查積壓的消息內容是否是某一類型的業務,用於確定這是否是某一個特殊的寫法導致的,以及判定問題的影響範圍。
3、 檢查MQ的日誌看是否收到了ACK應答,用於判斷是否是程序本身沒有簽收導致。檢查ACK超時時間等參數,是否配置不合理,導致簽收超時
4、 檢查生產者及每秒鐘生產消息的數量,檢查消費者及每秒鐘消費消息的數量,判斷是否是生產過快,消費過慢導致。
5 排查過程:
5.1 保護現場:
我遇到過的很多問題,不能在第一時間定位到故障原因,主要是因爲出現了問題以後現場被破壞導致,導致這個問題的主要原因是:
1、由於不好定位或者是因爲生產系統要快速恢復運營,系統馬上被重啓了;
2、自以爲需要的資料已經採集全了,快速重啓了系統。
3、由於日誌打印不全,認爲已經沒有有價值的材料可以收集了,快速重啓系統。
其實現代系統已經在HA方面做了很多工作。在出現問題的情況下,只要系統整體能夠提供服務的情況下,沒必要馬上進行重啓,大部分異常情況下是可以通過引流和隔離的方式,將流量引入正常節點,爲排查和分析過程留下更多的分析時間。
一般情況下,我們需要採集的資料包括但不限於:應用日誌、jvm線程和內存的dump文件(每隔10-30秒截取一次,建議截取3次)、中間件、數據庫日誌、cpu、物理內存、網絡帶寬、磁盤io等。
5.2 排查問題
系統出現的問題基本可分爲容易排查型(急症)和不容易排查型(慢症)兩種。急症類問題一般情況下日誌中的異常和系統異常的現象是直接相關聯的,病症好定位、好排查。慢症類問題一般只能看到崩潰或者是系統運行緩慢,這類問題只通過日誌很難排查定位。不論是急症還是慢症,我們要從異常的現象和日誌中通過系統的方式進行摸排,縮小可疑範圍,找到問題的根源。
5.2.1 分析及定位過程
5.2.1.1 步驟一 日誌檢查:
系統出了問題我們一般首要排查的就是日誌。絕大多數情況下的,在系統出現問題時都能找到一些如exception,除非日誌打印不規範,把exception都吃掉了。這種情況下我們一般直接根據日誌定位問題即可,如果能夠直接定位問題的我們一般都可以定義爲急症,這種情況下直接制定解決方案即可。
5.2.1.2 步驟二 資料收集與準備:
有些問題只通過日誌排查不太好定位,需要靠複雜的分析過程才能找到問題。分析過程建議要自上而下從系統設計入手,採用隔離法、替換法、對比法、排除法來進行。此時需要升級排查手段。
對於內存溢出類的問題定位我們主要收集的材料包括內存溢出的頻率週期、批量任務還是聯機任務等信息,使用到的算法、詳細設計;
對於系統運行慢等問題我們需要收集的資料包括部署方式、硬件及使用情況、使用到的算法、詳細設計、緩慢的時間段、時間點、交易特點等;
另外對於現有現象、及已經做過的各種測試及結論,要請項目組進行特別說明;
最好要準備一套測試環境,能夠進行模擬和還原現象,用於驗證問題點,及問題點排障後的效果
5.2.1.3 步驟三 分析與計劃:
分析的過程要採取大膽假設,仔細論證的指導方針。在未經實驗論證或者專家團有統一結論前,不要過早的排除任何可疑點。
第一步、我們需要將系統根據軟件和硬件、子系統和模塊的接口將系統進行劃分;劃分的過程主要是爲了方便排查過程中使用替換法、排除法、加壓減壓法、代碼走查法進行定位。對於複雜的系統,建議先做粗粒度的劃分,能有效的縮減排查範圍以及方便的進行隔離爲前提。排查之後,可以對某一懷疑部分進一步形成更細粒度的劃分。
第二步、對劃分好的區域範圍建立各種假設,最初的假設可以範圍大一些,可以在項目組中或專家團中徵求全部的意見,然後進一步劃分成更小的部分。
5.2.1.4 步驟四 執行排查:
執行排查之前建議將所有可疑點進行優先級排序,優先級按照可能性進行排序,可能性越高的優先排查。
首先、結合現有已知的現象進行分析,將前面列舉的可能存在的假設點進行排除;
其次、對優先級高假設點,優先安排測試計劃,驗證其有問題或者沒問題,以逐步縮小排查範圍;
最後、經過測試採集新的現象,並進行分析,之後調整假設點;
排查常用的方法:
1)、替換法:可以通過替換的過程,觀察替換前與替換後的情況,判定被點換的部件是否存在問題;
2)、排除法:對於有透傳或者一些通過配置可以摘掉的組件,可使用將其從系統中摘除後觀察其結果,判定其是否存在問題;
3)、加壓法或減壓法:通過增加壓力或者減小壓力的辦法加速測試結果的復現,如增加用戶數、減少等待的時間間隔、減少資源數量等方式,使懷疑點的狀態加以凸顯,進行排除;
4)、代碼走查:對可疑代碼組織專家團進行代碼走查;

5.2.1.5 步驟五 得到結論:
反覆執行步驟一到步驟四,直到找到一個假設點是導致整個問題的主因。此時注意,不要因爲找到這個主因就盲目下結論,進行修改。因爲很多時候我們見到的現象是由一連串複雜事件組合導致的。我們需要判斷是否可以從原理上解釋通我們之前收集到的所有現象,如果發現仍有現象無法解釋通,那麼此懷疑點可以做爲一個間接影響因素,可以在此基礎上縮小假設範圍繼續進行排查指導找到主因。並形成問題排查差報告。
5.2.2 常用排查技術及注意事項
5.2.2.1 日誌排查的技巧;
1、 日誌排查最直接的辦法是在應用日誌中搜索exception或者error字樣,不要漏掉任何exception。
2、 如果應用日誌中沒有打印以上信息,那就去weblogic、tomcat等容器的日誌中去找,如果還沒有就去這些容器啓動時轉輸出用的nohup.out中去找
3、 如果這幾個位置都沒有明顯的exception,那麼最大可能就是代碼中給吃掉了。
4、 有時我們可以看看warning級別的日誌,一般有些框架捕獲到異常後有些會以warning級別的日誌打印,這也是重要線索。常見的如文件系統空間不足,一般都是warning級別的日誌。
5、 日誌排查中對於可疑的交易,最好抓出來,按照線程號搜索一下交易從入口開始到結束時的所有打印信息。這可能會發現哪些交易中間卡住了。或者說那兩個步驟之間執行的緩慢耗時大。
6、 如果仍然定位不出問題,不要只排查問題出錯那一刻的日誌,要前推排查1天內的日誌或者從現象開始以後的所有日誌。
7、 日誌中如果出現很多異常信息,往往會只關注了最後的一個異常信息,其實很多時候是最後一個日誌信息並不是主因,而是一種表象,這裏我們要遵循時間線的關係,我們要根據錯誤發生的時間點,往前推,包括日誌沒打印,結合錯誤信息發生的時間點綜合分析,找到問題根源。
注意:不僅排查應用日誌、要一起配合排查數據庫、中間件等一起關聯繫統和關聯組件的日誌。因爲由於系統架構的特點導致,現象是有關聯關係的。這有助於我們分析和定位主要原因。
5.2.2.2 Jvm Dump文件的技術排查:
Jvm的dump主要有兩大類,上面我們已經做了介紹,因爲dump技術是快照技術,只能顯示當前時刻下系統的情況,建議採集dump文件時要連續採集多次,建議是時間間隔是10-30秒一次,共採集3-5次。分析dump文件不要只看一次採集的結果,要結合採集到的3-5次dump進行橫向比較jvm狀態。
1、 通過dump文件主要留意dump中年老代佔用的比例。如果年老代佔用比例走高,或者已經到了90%以上都有可能出現內存溢出。
2、 另外通過jmap命令可以查看當前內存中創建的對象數目,以及佔用的字節數,用於判斷是什麼類佔用了大量內存。
3、 另一類是jstack爲主的線程分析技術,主要通過觀察runnable、block、waiting三種線程狀態,找到性能瓶頸。其中重點關注BLOCKED狀態的數量,其次是WAITING狀態的數量。
4、 Jstack還有一個需要我們要注意的就是lock的數量,其中<>的括號中標註的是鎖號,如果出現大量的線程顯示waiting lock<>且鎖號相同,那麼性能瓶頸點很可能就在這裏。
5、 最特殊的是runnable如果出現大量線程停留在一個我們非常常見的方法時,也是也要提高警惕,很有可能這個類或者方法是非線程安全的,或者是由於遞歸或者特殊算法導致的無法退出。
5.2.2.3 數據庫慢SQL
數據庫慢SQL可以通過以下幾個辦法發現:
1、如果是oracle數據庫,可以找dba要AWR日誌,如果是其他數據庫可以問問dba有沒有相關日誌,這種方法可以查找到一個時間段範圍內慢SQL排名前20的執行次數、SQL內容、平均耗時時間。
2、可以通過數據庫session,查詢session執行時間長的,通過session號找到所對應的SQL語句。這個方法能找到此時慢的SQL語句是什麼。
3、如果使用了Druid連接器,可以使用它的監控界面查看慢SQL情況
5.2.2.4 數據庫執行計劃
目前大部分關係型數據庫都可以查詢sql語句的執行計劃,只需在sql語句前面加上Explain for命令 以oracle爲例:
Explain for select * from dual;
但是各種關係型數據庫的執行計劃的前綴和展示的結果略不相同,一下圖片是一個的典型oracle執行計劃輸出結果

對於一些特殊的客戶端可能顯示是這樣的

但是不論是哪一種,我們都可以看到幾個重要信息,參與sql的表的信息,是否走了索引,是否是全表掃描。這已經足夠我們分析sql慢的原因了。這個技術對於SQL優化非常有幫助。如果實在看不懂也可以找DBA進行幫助。

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