MySQL 9.7.0 开启 PGO 编译后,吞吐量最高提升 14.3%——Percona 基准测试拆解

2026-05-22 36 预计阅读时间:1 分钟
来源:percona.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:10 分钟

Oracle 在 MySQL 9.7.0 中首次发布了 PGO(Profile-Guided Optimization)编译版本。Percona 随即用 Sysbench OLTP Read/Write 对 PGO 和非 PGO 构建做了系统对比,覆盖 2GB、12GB、32GB 三档 InnoDB buffer pool,线程数从 1 到 512。结论很直接:PGO 版本在大部分场景下更快,最大提升 14.3%,平均 6.5%,而且性能增益的来源不是缓存或锁优化,而是 CPU 指令层面的效率提升。

三档 Buffer Pool 下的具体表现

测试分三个 Tier,每个 Tier 用不同大小的 InnoDB buffer pool 模拟不同内存压力:

Tier Buffer Pool 平均提升 最佳提升 回退情况
2G 2 GB 3.0% 5.5%(4 线程)
12G 12 GB 4.1% 8.6%(4 线程) 128 线程时 -3.1%
32G 32 GB 12.2% 14.3%(1 线程)

几个值得注意的规律:

  • 大内存场景收益最稳定。32GB Tier 在所有线程数下都保持 10.3%–14.3% 的提升,没有回退。这说明当内存充足、I/O 不构成瓶颈时,CPU 指令优化能充分释放。
  • 低并发区间增益最大。1–4 线程时三个 Tier 都达到各自峰值,因为此时单线程的指令路径效率直接决定了 QPS。
  • 小内存 + 高并发可能出现回退。12GB Tier 在 128 线程时出现了 -3.1% 的回退,512 线程时持平。PGO 优化的是"热路径"的指令布局,当并发压力改变代码执行频率分布,之前的优化可能不再对齐实际热点,甚至引入额外开销。

增益不是来自缓存或锁——是 CPU 指令层优化

Percona 对 InnoDB 内部指标做了深入对比,发现 PGO 和非 PGO 版本之间:

  • Buffer pool hit ratio 几乎完全一致
  • 锁竞争指标没有差异
  • I/O 量随吞吐量等比例增长,没有因为 PGO 而减少读写次数

换句话说,PGO 并没有让 MySQL "少干活",而是让同样的工作量在 CPU 上执行得更高效。具体机制:

  • 更好的分支预测:PGO 根据运行时 profile 标注分支的倾向性,编译器把大概率执行的分支放在顺序执行路径上,减少 CPU 分支预测失败。
  • 更紧凑的指令缓存利用:热路径函数被重新排列,让它们落在同一个 cache line,减少 icache miss。
  • 更精准的内联决策:编译器知道哪些函数调用频率高、体积小,优先内联它们,消除调用开销。
  • 指令调度优化:根据实际执行频率重新排列指令顺序,减少流水线气泡。

这些优化叠加起来,就是 6.5% 的平均吞吐提升——同样的硬件、同样的数据、同样的 SQL,只是二进制文件的指令布局不同。

如何自己构建 PGO 版本的 MySQL

MySQL 9.7.0 的 BUILD.md 已经描述了 PGO 构建流程。核心思路是两阶段编译:先插桩编译 → 用典型负载跑 profile → 再用 profile 数据重新编译。下面是一个可以在 Linux 上直接执行的构建脚本框架:

#!/usr/bash
# MySQL 9.7.0 PGO 构建示例(基于 BUILD.md 流程)
# 前置条件:已安装 cmake, gcc/clang, make, 以及 MySQL 构建依赖

MYSQL_SRC=/path/to/mysql-9.7.0
BUILD_DIR=/tmp/mysql-pgo-build
PROFILE_DIR=/tmp/mysql-pgo-profiles

# ===== 第一阶段:插桩编译 =====
mkdir -p "$BUILD_DIR/stage1" && cd "$BUILD_DIR/stage1"
cmake "$MYSQL_SRC" \
  -DCMAKE_BUILD_TYPE=Release \
  -DWITH_PGO=GENERATE \
  -DWITH_INNODB_BUFFER_POOL_SIZE=1G \
  -DCMAKE_INSTALL_PREFIX=/usr/local/mysql-pgo
make -j$(nproc)
make install

# ===== 第二阶段:收集 Profile =====
# 启动插桩版本,用典型工作负载跑一段时间
/usr/local/mysql-pgo/bin/mysqld --defaults-file=my-pgo.cnf &

# 用 sysbench 模拟 OLTP 负载(与 Percona 基准测试方法对齐)
sysbench oltp_read_write \
  --tables=20 \
  --table-size=5000000 \
  --threads=32 \
  --time=600 \
  --mysql-host=127.0.0.1 \
  --mysql-port=3306 \
  --mysql-user=root \
  run

# 停止 MySQL,profile 数据会写入 $PROFILE_DIR
mysqladmin -u root shutdown
# 插桩二进制运行时会在指定目录生成 .profdata 文件

# ===== 第三阶段:用 Profile 重新编译 =====
mkdir -p "$BUILD_DIR/stage2" && cd "$BUILD_DIR/stage2"
cmake "$MYSQL_SRC" \
  -DCMAKE_BUILD_TYPE=Release \
  -DWITH_PGO=USE \
  -DPROFILE_DIR="$PROFILE_DIR" \
  -DCMAKE_INSTALL_PREFIX=/usr/local/mysql-pgo-final
make -j$(nproc)
make install

echo "PGO 构建完成:/usr/local/mysql-pgo-final"

关键参数说明:

  • -DWITH_PGO=GENERATE:第一阶段,告诉编译器插入 profiling 指令。
  • -DWITH_PGO=USE:第二阶段,把收集到的 profile 数据喂给编译器做优化。
  • -DPROFILE_DIR:指定 profile 数据目录,具体路径取决于编译器和插桩运行时的输出配置,请参考 MySQL 源码中的 BUILD.md 确认。
  • 收集 profile 时的工作负载应尽量贴近你生产环境的真实查询模式。Percona 用的是 Sysbench OLTP Read/Write,如果你的业务以读为主或写密集,应调整 sysbench 参数或直接用真实流量。

用 Sysbench 复现基准测试

如果你想在自己的硬件上验证 PGO 效果,可以复现 Percona 的测试方法:

# 准备数据(20 张表,每张 500 万行)
sysbench oltp_read_write \
  --tables=20 \
  --table-size=5000000 \
  --threads=32 \
  --mysql-host=127.0.0.1 \
  --mysql-port=3306 \
  --mysql-user=root \
  prepare

# 逐线程数跑基准测试
for t in 1 4 16 32 64 128 256 512; do
  echo "=== Running with $t threads ==="
  # 先预热
  sysbench oltp_read_write \
    --tables=20 --table-size=5000000 --threads=$t \
    --time=600 --mysql-host=127.0.0.1 --mysql-port=3306 \
    --mysql-user=root --warmup-time=180 run > "result_pgo_t${t}.log"
done

# 对 PGO 和非 PGO 版本各跑一轮,对比 tps 数值

建议至少跑两轮取稳定值,并确保测试期间没有其他负载干扰。Percona 每个线程数跑 900 秒(15 分钟),预热 180–600 秒,这个时长足以让 buffer pool 和 OS cache 进入稳态。

采纳建议与注意事项

谁应该用 PGO 版本?

  • 内存 ≥ 32GB、以低中并发为主的 OLTP 场景——收益最大且无回退风险。
  • CPU 是瓶颈(高 QPS 单实例)的场景——PGO 直接提升每核吞吐。
  • 有能力自行构建和验证的用户——Oracle 官方 PGO 版本可以直接用,但自建版本可以用自己的业务负载做 profile,效果可能更好。

谁需要谨慎?

  • 内存较小(2–4GB buffer pool)且并发极高(128+ 线程)的场景——收益有限,12GB Tier 在 128 线程甚至出现了 -3.1% 回退。
  • 无法在预发布环境做充分验证的生产系统——PGO 的 profile 质量取决于采集负载的代表性,用 sysbench 采集的 profile 对 sysbench 负载最优,对你的业务不一定最优。

自建 PGO 的检查清单:

  1. Profile 采集负载是否覆盖了生产环境的典型查询模式?
  2. 采集时长是否足够(建议 ≥ 10 分钟稳态运行)?
  3. 是否在相同硬件上对比了 PGO 和非 PGO 版本?
  4. 是否测试了目标并发范围(特别是高线程数场景)?
  5. 是否监控了 InnoDB hit ratio、锁等待、I/O 延迟等指标,确认 PGO 版本没有引入异常?

PGO 不是银弹,6.5% 的平均提升在"零成本"优化里已经相当可观——不需要改 SQL、不需要调参数、不需要加硬件,只是换一个编译选项。但在高并发小内存场景下,务必先跑一轮自己的基准测试再决定是否上线。


相关推荐