使用火焰圖定位 OLAP 引擎瓶頸
在維護 OLAP 引擎時,很多時候需要對引擎做系統的性能分析和優化,此時往往需要查看 CPU 耗時,了解主要耗時點及瓶頸在哪里。俗語有曰:兵欲善其事必先利其器,程序員定位性能問題也需要一件“利器”。性能調優工具(perf)能夠顯示系統的調用棧及時間分布,但是呈現內容上只能單一的列出調用?;蛘叻菍哟位臅r間分布,不夠直觀?;鹧鎴D(flame graph)能夠幫助大家更直觀的發現問題。本文將以 Presto 為例,介紹火焰圖的使用技巧。
初識火焰圖
Perf 的原理是這樣子的:每隔一個固定的時間,就在 CPU 上(每個核上都有)產生一個中斷,在中斷上看看,當前是哪個 pid,哪個函數,然后給對應的 pid 和函數加一個統計值,這樣,我們就知道 CPU 有百分幾的時間在某個 pid,或者某個函數上了。而火焰圖(Flame Graph)是由 Linux 性能優化大師 Brendan Gregg 發明的,和所有其他的 profiling 方法不同的是,火焰圖以一個全局的視野來看待時間分布,它從底部往頂部,列出所有可能導致性能瓶頸的調用棧。
火焰圖整個圖形看起來就像一個跳動的火焰,這就是它名字的由來?;鹧鎴D有以下特征(這里以采樣CPU 火焰圖為例):
- 每一列代表一個調用棧,每一個格子代表一個函數。
- 縱軸展示了棧的深度,按照調用關系從下到上排列。最頂上格子代表采樣時,正在占用 CPU 的函數
- 橫軸的意義是指:火焰圖將采集的多個調用棧信息,并行關系。橫軸格子的寬度代表其在采樣中出現頻率,所以一個格子的寬度越大,說明它是瓶頸原因的可能性就越大。
- 火焰圖格子的顏色是隨機的暖色調,方便區分各個調用信息。
- 其他的采樣方式也可以使用火焰圖, 比如內存
所以,火焰圖就是看頂層的哪個函數占據的寬度最大。只要有”平頂”,就表示該函數可能存在性能問題,也是我們性能優化收益最大的地方。
Java生態常見的用于perf的工具有:allocation-instrumenter、YourKit Profiler、async-profiler、JProfiler、Arthas(基于 async-profiler )。筆者推薦使用阿里巴巴出品的 Arthas 或 async-profiler,筆者喜歡使用 async-profiler 這個 perf 工具生成火焰圖,主要原因是用法簡單,足夠滿足日常排查性能問題了。
async-profiler 介紹
Async-profiler是一款沒有 Safepoint bias problem 的低開銷 Java 采集分析器,它的實現是基于HotSpot 特有的 API,通過這些特有的 API 收集堆棧跟蹤和跟蹤內存分配,因而其可以和 OpenJDK、Oracle JDK 和其他基于 HotSpot JVM 的 Java 應用在運行時協同工作。
Async-profiler 可以跟蹤以下類型的事件:
- CPU 周期;
- 硬件和軟件性能計數器,如 cache misses, branch misses, page faults, context switches 等;
- Java 堆中的分配;
- 鎖嘗試,包括 Java 對象監視器和可重入鎖;
支持的平臺 - Linux / x64 / x86 / ARM / AArch64
- macOS / x64
并且 IntelliJ IDEA Ultimate 2018.3 之后的版本也集成了 async-profiler。Github 項目鏈接地址:https://github.com/jvm-profiling-tools/async-profiler
主頁上有已經編譯好的包,找到對應的平臺下載即可:
async-profiler 使用
下載好的文件解壓后,有一個profiler.sh腳本,運行腳本即可對 Java 進程進行 CPU 分析。例如 Presto 的 Java 進程 id 為 8983。
1
|
|
或者可以用-d指定剖析的時間(秒),我們將其重定向到一個文本文件里。
1
|
./profiler.sh -d 30 8983 > 8983.txt
|
執行完成后會輸出采集的信息,這些信息可以幫我看到詳細的函數調用邏輯,這非常有用,因為假如你新接觸一個新的 OLAP 引擎,想快速入門,比如想了解其讀寫流程及代碼執行流程,通過這些采集信息就可以快速幫我們捋清楚執行流程,如下:
如果你想生成火焰圖,只需要執行如下命令即可:
1
|
|
生成的svg火焰圖如下圖所示:
從上圖我們可以看到大約 91.63% 的CPU用于GC,如果當前系統 CPU 使用率比較高,那就說明這些 CPU 沒有在干正事,都耗費在 GC 里了,當前服務的主要瓶頸在 JVM 層,需要找下 JVM 瓶頸。
event 參數介紹
event 可選參數使用如下命令查看,不同 CPU 支持的 event 是不一樣的。
1
|
./profiler.sh list list 8983
|
輸出結果:
1
|
[presto@localhost ~]$ ./profiler.sh list list 8983
|
event 默認為 cpu ,假如想查看內存分配,可以使用 alloc 查看,命令如下:
1
|
./profiler.sh -e alloc -d 30 -f /tmp/flamegraph.svg 8983
|
火焰圖如下:
如上圖所示,可以看到內存占用主要包括三大部分:Presto執行、數據交互及讀取HDFS,知道這些信息后,我們就可以針對性的優化了。
使用過程中常見的問題
在使用 Async-profiler 過程中,我們有時候會遇到如下錯誤:
1
|
Could not start attach mechanism: No such file or directory
|
這是因為 Java 程序第一次執行 jmap 或 jstack 等 perf 命令后會在 /tmp 下創建 socket 文件,socket 文件路徑為 /tmp/.java_pidXXX,但是操作系統默認 10 天會刪除這個臨時文件,之后再執行 perf 命令就不行了,解決方法是在 /usr/lib/tmpfiles.d/tmp.conf 中添加 x /tmp/.java* ,這樣就不會刪除這個 socket 文件了。
參考資料:

浙公網安備 33010602011771號