GreptimeDB 内存分析实战基于 jeprof 的采样、监控与火焰图生成全流程【免费下载链接】greptimedbThe open-source observability database. One columnar engine for metrics, logs, and traces, on object storage.项目地址: https://gitcode.com/GitHub_Trending/gr/greptimedb本文是一份针对 GreptimeDB 的完整内存分析操作指南核心内容来自仓库中 docs/how-to/memory-profile-scripts/scripts/README.md 提供的内存分析流程并辅以同目录下的现成脚本与 src/common/mem-prof 模块的源码佐证。读者将掌握如何获取jeprof工具、如何在开启堆采样后运行 GreptimeDB、如何利用dump.sh自动监控内存增长并在超阈值时抓取.gprof采样文件、如何一键生成火焰图含相邻采样间的差值火焰图以及如何将火焰图交付给开发者进一步排查内存问题。整套流程无需改动任何代码全部基于仓库内现成脚本与 HTTP 调试接口即可完成。内存分析的整体流程按照 README 的定义GreptimeDB 的内存分析是一个“采样 → 监控 → 可视化 → 分析”的闭环共分为 5 个步骤获取jeprof工具脚本获取方法见下文“获取jeprof工具”小节。以环境变量MALLOC_CONFprof:true启动greptimedb后把greptimedb进程的 PID 作为参数执行dump.sh脚本。该脚本会持续监控内存使用并在超过阈值例如 10 分钟内增长超过 20MB时抓取内存画像输出形如greptime-{timestamp}.gprof的文件。积累 23 个.gprof文件后在相同环境下运行gen_flamegraph.sh生成展示内存分配调用栈的火焰图。注意gen_flamegraph.sh要求当前目录下存在jeprof和可选的flamegraph.pl。如需现场生成火焰图先运行get_flamegraph_tool.sh它会将火焰图生成工具flamegraph.pl下载到当前目录。将生成的火焰图即整个gprof_directory/flamegraphs目录发送给开发者供进一步分析。获取jeprof工具jeprof是 jemalloc 自带的内存画像解析工具负责把 jemalloc 堆采样 dump 文件解析成可读的调用栈数据。README 给出了三种获取方式从简单到复杂依次排列任选其一即可但必须与greptimedb将要运行的环境保持一致。方式一编译 GreptimeDB 源码后直接复用如果你是从源码编译 GreptimeDB那么jeprof已经在编译期间生成。执行cargo build之后运行仓库提供的 find_compiled_jeprof.sh 即可将其复制到当前目录。该脚本的逻辑非常简单在当前目录递归查找名为jeprof的文件找到后复制到当前目录并赋予可执行权限找不到则报错退出。其内部实现如下JPROF_PATH$(find . -name jeprof -print -quit) if [ -n $JPROF_PATH ]; then echo Found jeprof at $JPROF_PATH cp $JPROF_PATH . chmod x jeprof echo Copied jeprof to current directory and made it executable. else echo jeprof not found exit 1 fijeprof之所以在编译 GreptimeDB 时自动产出是因为 GreptimeDB 的 common-mem-prof/Cargo.toml 通过tikv-jemalloc-sys依赖启用了profiling与statsfeaturejemalloc 在构建其 C 库时会顺带生成jeprof工具。其典型路径位于./target/${PROFILE}/build/tikv-jemalloc-sys-${HASH}/out/build/bin/jeprof同时how-to-profile-memory.md 提醒如果从系统包管理器安装 jemalloc需要注意默认版本的jeprof可能没有--collapsed选项若选择包管理器方式请确保jeprof版本不低于 5.3.0。方式二通过 Cargo 新建最小工程快速生成如果你本地已经安装了 Rust 工具链可以用一个最小的 Cargo 工程来产出jeprofcargo new get_jeprof cd get_jeprof然后在Cargo.toml中追加依赖[dependencies] tikv-jemalloc-ctl { version 0.6, features [use_std, stats] }接着构建cargo build构建完成后jeprof工具即已产出。此时在当前目录运行find_compiled_jeprof.sh它会自动定位并把jeprof复制到当前目录。注意这里 Cargo.toml 中声明的是 0.6 版本而 GreptimeDB 源码内部见 common-mem-prof/Cargo.toml使用的是tikv-jemalloc-ctl 0.7并同时启用use_std与statsfeatures这是为了匹配 GreptimeDB 自身的 jemalloc 构建选项。方式三从源码编译 jemalloc最复杂但最可控的方式是从源码编译 jemalloc。先克隆 tikv/jemalloc 仓库并切换到指定提交git clone https://github.com/tikv/jemalloc.git cd jemalloc git checkout e13ca993e8ccb9ba9847cc330696e02839f328f7然后执行经典的三步构建./configure make构建完成后jeprof位于.bin/目录中将其复制到当前目录即可。采集阶段用dump.sh自动监控内存并抓取画像dump.sh是整套流程中的“监控采集”核心脚本其完整实现位于 dump.sh。它本质上是一个无限循环守护脚本每 10 分钟检查一次目标进程的内存占用当内存增量超过 20MB 时就通过 HTTP 接口抓取一次内存画像。关键参数参数默认值说明内存增长阈值threshold_kb20MB20 * 1024KB当进程内存较上次检查增长超过该值时触发 dump检查间隔sleep_interval600 秒10 分钟每次循环后休眠时长进程 PID命令行参数$1被监控的greptime进程的 PID画像输出greptime-{timestamp}.gprof通过curl抓取 HTTP dump 结果落盘运行方式./dump.sh PID脚本启动后会先校验 PID参数缺失、非纯数字、或ps无法读取到该进程的 RSS 时都会给出明确提示进程短暂不可读时会跳过本轮检查而不会误触发 dump保留上一次last_mem_kb以避免误报。内部工作机理每轮循环的执行逻辑为通过ps -o rss -p $pid获取当前常驻内存 RSS单位 KB计算与上一轮记录的last_mem_kb的差值diff_kb若diff_kb大于threshold_kb20MB则生成时间戳并执行curl -sf -X POST localhost:4000/debug/prof/mem greptime-${timestamp}.gprof成功则提示画像已保存失败则删除可能为空的文件并打印 curl 退出码 4. 更新last_mem_kb为当前值休眠 600 秒后进入下一轮。这里curl -X POST localhost:4000/debug/prof/mem命中的是 GreptimeDB HTTP 服务中的内存画像 dump 接口。从 servers 的 HTTP 路由源码 可以看到/debug/prof命名空间下注册了一组调试路由POST /memdump 画像、GET /mem/status查询状态、POST /mem/activate/POST /mem/deactivate启停堆采样、GET/POST /mem/gdumpgdump 开关与状态查询、POST /mem/symbol外部 dump 文件符号化。在底层这些接口由 src/common/mem-prof/src/jemalloc.rs 实现dump_profile通过tikv_jemalloc_ctl::raw::write(PROF_DUMP, ptr)触发 jemalloc 的prof.dump控制变量将当前堆采样写入临时文件后读出返回字节流而curl端收到的正是这份 jemalloc heap dump 的原始字节。因此dump.sh采集出的.gprof文件本质上是 jemalloc 格式的堆画像文件。注意只有在启动 GreptimeDB 时开启了prof:true见下一节prof.dump才可用。dump_profile内部会先通过opt.prof控制变量校验采样是否已启用未启用时直接返回ProfilingNotEnabled错误。使用建议监控期间保持 GreptimeDB 正常承载业务流量以捕捉真实的、与业务相关的内存增长模式积累 23 个.gprof文件即可进入下一阶段——这正是gen_flamegraph.sh生成差值火焰图所需的最少样本数若进程内存持续平稳增长则每个 dump 文件都能反映一段窗口内的分配调用栈非常适合定位“内存缓慢性增长/泄漏”类问题。开启内存采样启动带prof:true的 GreptimeDBdump.sh能采集到数据的前提是 GreptimeDB 以开启堆采样的方式启动。参考 how-to-profile-memory.md启动方式如下# Linux MALLOC_CONFprof:true ./target/debug/greptime standalone start # macOS _RJEM_MALLOC_CONFprof:true ./target/debug/greptime standalone start其中MALLOC_CONFprof:true是 jemalloc 的标准堆采样开关macOS 下 tikv-jemalloc 使用_RJEM_MALLOC_CONF前缀。不开启该开关时/debug/prof/mem接口会直接报错。此外官方 Docker 镜像默认已启用并激活内存采样。这一行为由enable_heap_profiling配置项控制[memory] # Whether to enable heap profiling activation during startup. # Default is true. enable_heap_profiling true将其设为false即可在镜像中关闭内存采样。运行时控制接口可选如果不想重启进程也可以借助 HTTP 接口在运行时启停采样与查询状态# 查询当前采样状态 curl -X GET localhost:4000/debug/prof/mem/status # 激活堆采样若尚未激活 curl -X POST localhost:4000/debug/prof/mem/activate # 停用堆采样 curl -X POST localhost:4000/debug/prof/mem/deactivate # 激活 gdump每当虚拟内存使用超过历史峰值时自动 dump 画像 curl -X POST localhost:4000/debug/prof/mem/gdump -d activatetrue # 停用 gdump curl -X POST localhost:4000/debug/prof/mem/gdump -d activatefalse # 查询 gdump 当前状态 curl -X GET localhost:4000/debug/prof/mem/gdump这些接口对应 jemalloc.rs 中的activate_heap_profile/deactivate_heap_profile/set_gdump_active/is_gdump_active等函数它们分别读写 jemalloc 的prof.active与prof.gdump控制变量。手动 dump可选dump.sh之外也可以随时手动抓取一份画像并直接输出为不同格式# 原始 jemalloc heap dump curl -X POST localhost:4000/debug/prof/mem greptime.hprof # 直接输出火焰图 SVG curl -X POST localhost:4000/debug/prof/mem?outputflamegraph greptime.svg # 输出 pprof 格式 curl -X POST localhost:4000/debug/prof/mem?outputproto greptime.pprof从源码看dump_pprof与dump_flamegraph会先调用dump_profile拿到 jemalloc dump再借助jemalloc-pprof-utilspprof_util解析为StackProfile进而转换为 pprof 或火焰图。这也解释了为什么同一个接口能输出三种格式。可视化阶段用gen_flamegraph.sh生成火焰图火焰图是分析内存分配调用栈最直观的形式横轴表示采样到的分配纵轴表示调用栈层级色块宽度代表该路径上的分配占比能一眼定位“内存被谁占走”。前置条件gen_flamegraph.sh要求当前目录下同时存在两个工具./jeprof解析.gprof文件./flamegraph.pl把 collapsed 格式的栈数据渲染为 SVG 火焰图。若缺少flamegraph.pl先运行 get_flamegraph_tool.sh 下载./get_flamegraph_tool.sh该脚本本质就是一行 curl 加授权curl https://raw.githubusercontent.com/brendangregg/FlameGraph/master/flamegraph.pl ./flamegraph.pl chmod x ./flamegraph.pl脚本启动时会分别校验./jeprof与./flamegraph.pl是否存在任一缺失都会报错退出set -e保证任何一步失败即终止。使用方式Usage: ./gen_flamegraph.sh binary_path gprof_directorybinary_pathgreptimedb可执行文件的路径解析采样时需要符号信息因此必须是同一份二进制gprof_directorydump.sh输出.gprof文件的目录。示例调用./gen_flamegraph.sh ./greptime .生成火焰图可能需要几分钟时间。产物位于gprof_directory/flamegraphs目录如果当前目录没有flamegraph.pl则该目录下只有.collapse文件collapsed 格式的栈计数文本同样可用于后续分析。脚本做了什么从 gen_flamegraph.sh 的实现看它对目录内所有.gprof文件按文件名自然排序逐一执行两件事单文件火焰图——等价于./jeprof binary gprof --collapse | ./flamegraph.pl output即先用jeprof ... --collapse把堆画像折叠成栈计数文本再交给flamegraph.pl渲染成filename.svg。相邻差值火焰图diff 分析——从第二个文件起对每一对相邻画像执行./jeprof binary --base gprof1 gprof2 --collapse | ./flamegraph.pl output_diff--base模式计算两次采样之间的增量分配产物命名为prev_vs_next_diff.svg。这正是 README 建议“积累 23 个 gprof 文件”再运行脚本的原因差值火焰图能精确揭示两个时间点之间新增的内存分配来自哪些调用路径是定位内存泄漏的利器。最终flamegraphs目录中每个样本都会产出对应的.collapse与.svg相邻样本之间还会产出_diff.collapse与_diff.svg。从 .collapse 单独生成火焰图如果手头只有.collapse文件例如在无flamegraph.pl的环境中先完成了 collapse 折叠可以使用 gen_from_collapse.sh 补出 SVGUsage: ./gen_from_collapse.sh collapse_directory它会扫描指定目录下所有.collapse文件逐个用./flamegraph.pl渲染为同名.svg。交付与分析将 flamegraphs 目录交给开发者README 明确建议将生成的火焰图整个gprof_directory/flamegraphs文件夹发送给开发者做进一步分析。这样做的原因在于火焰图 SVG 携带了完整的调用栈语义开发者可以快速比对不同时间点的快照以及相邻差值图判断内存增长是否集中在某条特定调用路径flamegraphs目录中还保留了.collapse中间产物便于在本地用其他工具如 Brendan Gregg 的 FlameGraph 系列脚本做二次加工如需复现或深挖配合对应的greptimedb二进制与.gprof原始文件开发者可以在自己环境中重新生成任意火焰图甚至在支持符号化的环境中获取更精确的函数名。进阶外部 dump 文件的符号化除了解析 GreptimeDB 自身 HTTP 接口产出的画像how-to-profile-memory.md 还介绍了另一条实用路径外部生成、内部符号化。如果堆 dump 文件是在外部环境生成的例如通过MALLOC_CONFprof:true,prof_prefix:jeprof.out,lg_prof_interval:26或prof_gdump机制自动落盘可以把它上传到运行中的 GreptimeDB 实例做符号化并直接得到火焰图curl -X POST --data-binary /path/to/jeprof.out.12345.0.i0.heap \ localhost:4000/debug/prof/mem/symbol flamegraph.svg该接口对应 mem_prof.rs 中的symbolicate_handler底层调用 jemalloc.rs 的symbolicate_jeheap利用当前进程的内存映射jemalloc_pprof_mappings::MAPPINGS解析 jeheap 格式文件再渲染为火焰图 SVG。这在以下场景尤其有用在无调试符号的生产环境采集到了堆 dump希望拿到带符号的火焰图希望用 jemalloc 自带的自动 dump 机制prof_prefixlg_prof_interval或gdump批量采集再统一上传符号化需要分析非 GreptimeDB HTTP 接口产出的历史 dump 文件。排查思路小结结合上述全部工具一条完整的 GreptimeDB 内存问题排查路径可以总结为以MALLOC_CONFprof:true启动 GreptimeDB或直接使用官方 Docker 镜像其默认已启用采样用find_compiled_jeprof.sh取出jeprof必要时用get_flamegraph_tool.sh补上flamegraph.pl用dump.sh PID在业务运行期间自动监控并抓取多份greptime-{timestamp}.gprof积累 23 份画像后运行./gen_flamegraph.sh binary gprof_dir产出单文件火焰图与相邻差值火焰图将整个flamegraphs目录交给开发者结合差值火焰图定位新增内存分配的调用栈最终收敛到具体模块若只有外部 jemalloc dump可利用/debug/prof/mem/symbol接口符号化。整套方案零代码侵入、全部依赖仓库内现成脚本dump.sh、gen_flamegraph.sh、find_compiled_jeprof.sh、get_flamegraph_tool.sh、gen_from_collapse.sh与 HTTP 调试接口既可用于开发环境的问题复现也适合生产环境的低侵入式排查。【免费下载链接】greptimedbThe open-source observability database. One columnar engine for metrics, logs, and traces, on object storage.项目地址: https://gitcode.com/GitHub_Trending/gr/greptimedb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
