開發與維運

clickhouse與火焰圖

最近再對clickhouse hive engine進行一些調優工作。本文將總結調優過程中常用的一些工具和命令。

火焰圖


火焰圖 (Flame Graph) 是性能優化大師 Bredan Gregg 創建的一種性能分析圖標,因為它的樣子近似火焰而得名。使用火焰圖能夠非常快速的定位到代碼中的瓶頸,它就像一個在代碼之海中航行的程序員的地圖,指引著性能優化的方向。下圖是clickhouse的一張火焰圖。

image.png

火焰圖的上下表示調用關係,下層的函數調用上層的函數。每個函數對應一個等高的長條,這個長條對應著該函數的某項指標,如cpu time/real time/memory allocate.

根據不同的指標,火焰圖可以分為:

  • cpu火焰圖。顯示代碼中函數的cpu耗時,函數寬度與cpu time成正比。主要用於分析進程中的cpu瓶頸

  • real火焰圖。顯示代碼中函數的duration耗時,函數寬度與real time成正比。與cpu火焰圖結合可用於分析進程中的io瓶頸

  • mem火焰圖。顯示代碼中函數中申請內存的大小,函數寬度與memory allocation成正比。主要用於優化進程的內存佔用。

對於clickhouse來說,性能瓶頸主要在於cpu和io。因此本文著重介紹如何生成cpu和real火焰圖

clickhouse-flamegraph


clickhouse-flamegraph(https://github.com/Slach/clickhouse-flamegraph)是一個命令行工具,他能夠將clickhouse系統表system.trace_log中的性能指標以query_id為粒度可視化,形成對應的火焰圖。

在使用clickhouse-flamegraph之前,需要

  • 安裝:參考官方文檔,此處略

  • 配置:修改clickhouse配置文件:config.xml和users.xml,參考官方文檔。這樣做的目的是讓clickhouse在query執行過程中,對cpu time, real time, memory allocate等指標進行採樣。

安裝和配置完成後,如何使用clickhouse-flame-graph生成火焰圖呢?

  • 首先執行對應的query,

  • 然後記錄對應的query_id

  • 運行clickhouse-flamegraph
$ clickhouse-flamegraph --dsn="http://localhost:8123/?user=default&password=xxx" --query-id=<query_id>
{"level":"info","dsn":"http://localhost:8123/?user=default&password=xxx","time":"2022-06-10T10:28:44+08:00","message":"connected to ClickHouse"}
{"level":"info","sqlFiles":1,"time":"2022-06-10T10:28:44+08:00","message":"write .sql files"}
{"level":"info","processedFiles":2,"time":"2022-06-10T10:28:44+08:00","message":"done processing"}

  • 最後查看火焰圖。在當前目錄下執行,可以看到以下幾個文件


$ tree clickhouse-flamegraphs 
clickhouse-flamegraphs
└── f
    ├── 2694aa7a-f421-4727-a789-383a7bce05be.CPU.svg
    ├── 2694aa7a-f421-4727-a789-383a7bce05be.CPU.txt
    ├── 2694aa7a-f421-4727-a789-383a7bce05be.MemorySample.svg
    ├── 2694aa7a-f421-4727-a789-383a7bce05be.MemorySample.txt
    ├── 2694aa7a-f421-4727-a789-383a7bce05be.Memory.svg
    ├── 2694aa7a-f421-4727-a789-383a7bce05be.Memory.txt
    ├── 2694aa7a-f421-4727-a789-383a7bce05be.Real.svg
    ├── 2694aa7a-f421-4727-a789-383a7bce05be.Real.txt
    ├── 2694aa7a-f421-4727-a789-383a7bce05be.sql

其中,*.Memory.svg對應著mem火焰圖,*.CPU.svg對應著cpu火焰圖,*.Real.svg對應著real火焰圖。*.sql中是本次查詢的query語句。

我們看到,使用clickhouse-flamegraph生成火焰圖還是很方便的。但是由於clickhouse的cpu time/real time的採樣原理是在每個線程中一方面通過定時器每隔一定時間發出SIGUSR1和SIGUSR2 signal,另一方面註冊信號處理函數,在收到signal的同時,將當前stack trace記錄到system.trace_log中去。

由於clickhouse自帶cpu time/real time的實現原理,clickhouse-flamegraph有兩個缺點

  • cpu time和real time的採樣時間間隔太長。以筆者調試hive engine查詢的經驗,目前20ms是比較可靠的間隔,間隔再短點就無法保證query正常執行。

  • 開啟cpu time和real time採樣會明顯拖慢query運行速度,導致火焰圖失真。

perf


perf是內置於linux kernel中的profiling工具,它能監測各種硬件和軟件事件,被廣泛用於性能瓶頸的查找和熱點代碼的定位。正因為他是內置的profiling工具,perf能夠避免clickhouse-flamegraph的的上述缺點。perf的缺省採樣頻率是4000/s, 使用perf也不會導致被觀測進程產生明顯的性能衰減。

那麼如何使用perf生成cpu和real火焰圖呢?

  • 首先在clickhouse加上以下編譯參數,避免debug信息丟失導致火焰圖不準確。

clickhouse在使用-O2編譯的時候,會默認帶上-fomit-frame-pointer和-fptimize-sibling-calls參數。這些參數通過編譯階段的優化,對運行是的性能有所提升,但是會讓debug信息變得不完整。

**-fomit-frame-pointer**

Don't keep the frame pointer in a register for functions that don't need one. This avoids the instructions to save, set up and restore frame pointers; it also makes an extra register available in many functions. It also makes debugging impossible on some machines.

-foptimize-sibling-calls

This option determines whether the compiler optimizes tail recursive calls. It enables conversion of tail recursion into loops.

If you do not want to optimize tail recursive calls, specify -fno-optimize-sibling-calls.

Tail recursion is a special form of recursion that doesn't use stack space. In tail recursion, a recursive call is converted to a GOTO statement that returns to the beginning of the function. In this case, the return value of the recursive call is only used to be returned. It is not used in another expression. The recursive function is converted into a loop, which prevents modification of the stack space used.


if (COMPILER_CLANG)
    # generate ranges for fast "addr2line" search
    if (NOT CMAKE_BUILD_TYPE_UC STREQUAL "RELEASE")
        set(COMPILER_FLAGS "${COMPILER_FLAGS} -gdwarf-aranges")
    endif ()
    if (HAS_USE_CTOR_HOMING)
        # For more info see https://blog.llvm.org/posts/2021-04-05-constructor-homing-for-debug-info/
        if (CMAKE_BUILD_TYPE_UC STREQUAL "DEBUG" OR CMAKE_BUILD_TYPE_UC STREQUAL "RELWITHDEBINFO")
            set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -fuse-ctor-homing")
            set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Xclang -fuse-ctor-homing")
        endif()
    endif()
    
    # 添加以下兩行
    # 
    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -fno-optimize-sibling-calls")
    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -fno-optimize-sibling-calls")
endif ()

  • 通過clickhouse-benchmark運行要profile的query。如

./clickhouse benchmark -i 5 --query "select day, hour, appid, count(1) from test.csc_like_all_push_hour_text where day = '2022-02-03' and hour = '00' group by day, hour, appid"

  • 在運行query的同時,執行perf record, 分別對cyclescpu-clock進行採集。其中cycles事件對應cpu time, cpu-clock事件對應real time. 如
perf record -ag -p <clickhouse-pid>  -e  cycles  -- sleep  15
perf record -ag -p <clickhouse-pid>  -e  cpu-clock  -- sleep  15
perf script > out.perf && stackcollapse-perf.pl out.perf > out.folded && flamegraph.pl out.folded > out.svg

perf生成的cpu火焰圖的示例:

image.png

perf生成的real火焰圖的示例:

image.png

總結


本文介紹了兩種為clickhouse進程生成火焰圖的方法:clickhouse-flamegraph與perf。clickhouse-flamegraph的優點是操作簡單,缺點是被觀測進程有性能衰減、採樣間隔太長。

perf在使用上更通用且更精確,但是操作也更復雜。因此建議將clickhouse-flamegraph用於定性分析,快速判定哪個模塊是瓶頸;將perf用於定量分析,定位哪個具體函數是瓶頸,並衡量性能優化後的收益。

Leave a Reply

Your email address will not be published. Required fields are marked *