如何使用Valgrind/perf定位PostgreSQL源码缓存未命中问题
缓存未命中检测操作指南
前置准备(必须完成)
首先确认你的PostgreSQL编译参数满足符号映射要求,仅--enable-debug不足,需额外添加编译参数-g -fno-omit-frame-pointer,不要开启-O3、-flto这类会优化掉函数/行号映射的参数。编译完成后可执行file /usr/local/postgresqlv14/bin/postgres确认输出包含debug_info字样。
方法1:perf(优先推荐,适用于真实TPC-C负载)
perf对程序运行性能影响仅5%以内,不会导致HammerDB测试结果失真,支持全层级缓存未命中统计与源码级标注。
操作步骤:
- 正常启动PostgreSQL服务:
/usr/local/postgresqlv14/bin/postgres -D /usr/local/postgres/data & - 提取PostgreSQL主进程PID:
PG_PID=$(ps aux | grep postgres | grep 'D /usr/local/postgres/data' | awk '{print $2}') - 启动缓存事件采样,采样时长和你要跑的TPC-C测试时长保持一致(示例为300秒,可自行调整),期间正常启动HammerDB测试即可:
perf record -e L1-dcache-load-misses,L1-dcache-store-misses,L2-cache-load-misses,L2-cache-store-misses,LLC-load-misses,LLC-store-misses -g -p $PG_PID -- sleep 300 - 测试结束后查看统计结果:
- 函数级缓存未命中统计:
perf report --sort comm,dso,symbol,可直接看到每个PostgreSQL内部函数的缓存未命中占比 - 源码级标注:选中对应函数后回车,选择
Annotate选项,或直接执行perf annotate <函数名>,即可看到每行C源码对应的缓存未命中计数。
方法2:Cachegrind(适用于低负载/单进程场景)
你之前操作失败的核心原因是未配置多进程输出聚合,且Cachegrind默认会为每个PostgreSQL fork的子进程生成独立统计文件,操作步骤:
- 停止所有运行中的PostgreSQL进程,避免端口冲突
- 用Cachegrind启动PostgreSQL主进程:
valgrind --tool=cachegrind --trace-children=yes --child-silent-after-fork=yes --cachegrind-out-file=cachegrind.out.%p /usr/local/postgresqlv14/bin/postgres -D /usr/local/postgres/data - 另开终端启动HammerDB低并发TPC-C测试,测试完成后停止PostgreSQL服务
- 合并所有子进程的统计文件:
cg_merge -o cachegrind.out.total cachegrind.out.* - 查看带源码标注的统计结果,替换为你本地PostgreSQL源码的根路径:
cg_annotate --auto=yes --source=/path/to/postgresql-v14/source/root cachegrind.out.total
常见注意事项
- Cachegrind会让程序运行速度降低10~100倍,仅适合低并发场景验证问题,不要用来跑高并发TPC-C基准测试,否则结果完全失真
- 若perf采集的调用栈不完整,可将
-g参数替换为--call-graph dwarf,该参数会输出更完整的调用栈,但会增大perf.data文件体积 - 若源码行号对应不上,重新编译PostgreSQL时不要strip二进制文件,保留完整调试符号即可。
内容的提问来源于stack exchange,提问作者arjunshetty955
相关产品推荐
相关产品推荐

