很多团队在上线前才临时”用手点一下”,凭感觉判断数据库”应该扛得住”。真正的容量规划需要可复现的数字:数据库压测就是用 sysbench 和 pgbench 这样的基准工具,把 QPS、TPS、p99 延迟变成可以横向对比的基线。本文带你从零跑通 MySQL/MariaDB 与 PostgreSQL 的压测全流程,并把它接进 CI 做持续容量守护。
一、压测前先想清楚三件事
在敲命令之前,先回答三个问题,否则压出来的数字既不好看也没用:
- 目标是什么:验证单实例上限?对比索引优化前后差异?还是为扩容找依据?目标不同,加压方式完全不同。
- 基线在哪儿:先在不加压时记录空闲指标(CPU、磁盘 IO、连接数),有了基线才能判断”掉点是被压出来的还是本来就慢”。
- 变量只动一个:每次只改一个因素(比如是否开慢查询、是否换 SSD),其余配置锁死,结论才站得住。
二、sysbench 压测 MySQL / MariaDB
sysbench 是跨数据库、跨场景的基准瑞士军刀,内置 oltp_read_write、oltp_read_only、oltp_write_only 等 Lua 脚本,最适合快速拿到 MySQL 系的吞吐与延迟曲线。
2.1 准备测试库与数据
先建一个独立的测试库,再灌入 10 张表、每张 10 万行作为初始数据集。生产库绝不直接压。
# 准备阶段:建表 + 灌数据(--threads 控制并发灌入)
sysbench oltp_read_write \
--db-driver=mysql \
--mysql-host=127.0.0.1 --mysql-port=3306 \
--mysql-user=bench --mysql-password=benchpwd \
--mysql-db=sbtest \
--tables=10 --table-size=100000 \
--threads=8 \
prepare
2.2 跑 OLTP 读写混合
正式加压用 run,时长建议 300 秒以上,让缓存预热、抖动收敛后再看稳态值。
sysbench oltp_read_write \
--db-driver=mysql \
--mysql-host=127.0.0.1 --mysql-port=3306 \
--mysql-user=bench --mysql-password=benchpwd \
--mysql-db=sbtest \
--tables=10 --table-size=100000 \
--threads=32 --time=300 --report-interval=10 \
run
输出里最该盯的是 transactions(总事务数)、queries(总查询数)和尾部的 latency 分布。把 threads 从 8 逐步加到 64、128,你就能画出一条”并发-吞吐”曲线,找到拐点。
2.3 只读 / 只写拆开看瓶颈
读写混在一起时,瓶颈可能是磁盘也可能是锁。拆成两种场景分别跑,能定位是 CPU 密集还是 IO 密集:
# 只读(验证缓存命中与读扩展能力)
sysbench oltp_read_only --threads=64 --time=300 run
# 只写(验证 redo/undo、磁盘刷盘能力)
sysbench oltp_write_only --threads=64 --time=300 run
2.4 读懂 sysbench 的关键输出
一轮 run 结束,sysbench 会打印汇总。下面是一段典型的只读测试结果,重点看三处:
SQL statistics:
queries performed:
read: 4820000
write: 0
other: 0
total: 4820000
transactions: 301250 (1004.17 per sec.)
queries: 4820000 (16066.74 per sec.)
Latency (ms):
min: 3.21
avg: 31.86
max: 412.50
95th percentile: 78.42
sum: 9598000.00
transactions 行括号内的数字就是 TPS;Latency 段的 95th percentile 是 p95 延迟,max 则是极端尾延迟。当并发继续加大,若 TPS 不再上升而 avg 与 p95 同步飙升,说明已到系统拐点,再加压只会更慢。把这段数字和前面阶梯加压的结果连起来,就是一份可交付的容量报告,也能直接作为 CI 门禁的对比基线。
| 关键参数 | 作用 | 建议 |
|---|---|---|
| –threads | 并发客户端数 | 从 8 阶梯加到超过 CPU 核数 |
| –time | 单轮时长(秒) | ≥ 300,避免短时抖动 |
| –table-size | 单表行数 | 越大越接近真实数据集 |
| –report-interval | 中间结果间隔 | 10s,观察是否收敛 |
三、pgbench 压测 PostgreSQL
PostgreSQL 自带 pgbench,内置经典的 TPC-B 类事务脚本(含 UPDATE、INSERT、SELECT),也能用 -f 跑你自己的业务 SQL。
3.1 初始化与内置压测
# 初始化测试库,-s 控制数据规模(scale factor)
pgbench -h 127.0.0.1 -U bench -d sbtest -i -s 100
# 跑 300 秒、64 并发的内置 TPC-B 事务
pgbench -h 127.0.0.1 -U bench -d sbtest \
-c 64 -j 8 -T 300 -P 10
3.2 用自定义脚本贴近真实业务
内置脚本毕竟是通用负载。把真实查询写成 SQL 文件,用 -f 指定,才能让压测反映你业务的真实读写比:
\set aid random(1, 100000)
\set bid random(1, 1000)
SELECT abalance FROM pgbench_accounts WHERE aid = :aid;
UPDATE pgbench_accounts SET abalance = abalance + 1 WHERE aid = :aid;
把上面内容存为 workload.sql,执行 pgbench -f workload.sql -c 64 -T 300 即可。注意 -c 是客户端连接数、-j 是工作线程数,二者配合才能打满连接池。
| 维度 | sysbench(MySQL 系) | pgbench(PG 系) |
|---|---|---|
| 数据准备 | prepare 子命令 | -i 初始化 |
| 并发参数 | –threads | -c 连接 / -j 线程 |
| 自定义负载 | 自带 Lua 脚本有限 | -f 写任意 SQL |
| 输出重点 | 事务/查询吞吐 + 延迟分布 | tps + 延迟 |
四、关键指标到底怎么读
压测报告一大堆数字,真正决定容量结论的其实就四个:
| 指标 | 含义 | 怎么用 |
|---|---|---|
| QPS / TPS | 每秒查询 / 事务数 | 衡量绝对吞吐,看拐点 |
| p95 / p99 延迟 | 尾延迟 | 比平均值更能暴露卡顿 |
| 延迟抖动 | 不同时段最大最小差 | 判断稳定性 |
| 错误率 | 超时 / 连接失败占比 | 超过 1% 即视为过载 |
平均值会骗人:平均 5ms 可能掩盖了 1% 请求 2 秒的灾难。容量规划一律以 p99 为准,再留 30% 余量。
五、压测最常见的 5 个坑
- 压测机比数据库还弱:压测客户端 CPU 打满,数字自然上不去,先确认压测机不是瓶颈。
- 没预热:缓存冷的瞬间吞吐极低,前 60 秒数据要丢弃。
- 关了持久化:为了数字好看关掉 fsync / redo,结果和线上完全两样,结论无效。
- 网络和线上不一致:本地回环压测延迟失真,至少用同可用区机器。
- 只看平均值:务必看 p99 与错误率,否则上线即翻车。
六、把压测写进 CI,做持续容量守护
一次性压测只能说明当下。把”固定并发下的 TPS 不低于基线 90%”做成回归门禁,每次改 schema 或加索引都能自动发现性能回退:
# .github/workflows/bench.yml 片段
- name: Run DB benchmark gate
run: |
TPS=$(sysbench oltp_read_write --threads=32 --time=120 run \
| grep "transactions:" | awk '{print $3}')
python3 -c "import sys; sys.exit(0 if float('$TPS') >= BASELINE*0.9 else 1)"
配合 Docker 起一个干净数据库实例,每次跑在隔离环境里,结果才可复现。这也是 用 Docker 快速搭隔离测试环境 的价值所在。
七、一个真实的容量规划案例
某订单服务单机 MySQL 在 32 并发下 TPS 约 4200,但 p99 延迟随并发上升陡增。排查发现热点表缺少合适索引,MySQL 索引底层原理 里提到的回表放大了随机 IO;补索引后 TPS 提升到 6800。再用 PostgreSQL 慢查询分析 同思路复核只读副本,最终把连接池按 HikariCP 调优 的”连接数 ≈ 核心数×2 + 磁盘数”经验值设定,整体稳定性显著改善。整个过程遵循 数据库设计规范 的命名与建模约定,避免了一开始就埋坑。压测不是终点,而是把”感觉”变成”数字”的闭环。
八、落地清单
- 明确目标与基线,变量只动一个;
- 用 sysbench / pgbench 阶梯加压,画并发-吞吐曲线;
- 只读、只写分开跑,定位 CPU 还是 IO 瓶颈;
- 以 p99 延迟 + 错误率为容量判据,留 30% 余量;
- 把压测做成 CI 门禁,持续守护性能。
数据库压测的价值不在跑分本身,而在把模糊的”应该没问题”变成可量化、可复现、可回归的基线。下次扩容或上索引前,先让 sysbench 和 pgbench 帮你说话。




