1. 基准测试的边界认知
在使用 sysbench 之前,必须先明确基准测试能回答什么、不能回答什么,否则很容易得出错误结论。
能回答的问题:
- 当前硬件和配置下,MySQL 的 TPS/QPS 上限是多少
- 两套配置方案之间的相对性能差异(如调整
innodb_buffer_pool_size前后) - 某次参数调整或版本升级后,性能是否有可量化的提升或退步
- 目标并发量下,响应延迟是否在可接受范围内
不能回答的问题:
- 生产环境的真实性能(基准负载是理想化的,生产 SQL 复杂得多)
- 长期运行稳定性(基准测试通常持续分钟级)
- 热点、锁竞争、主从延迟等业务相关的性能问题
sysbench 的结果是性能的参考基线,而非生产能力的直接映射。同一套硬件,sysbench 跑出 5 万 TPS,生产复杂查询下可能只有几千 QPS。
2. 安装
| |
安装后路径:主程序 /usr/bin/sysbench,测试脚本 /usr/share/sysbench/。
3. 第一阶段:数据准备(prepare)
3.1 创建测试库与专用账号
| |
不要用 root 账号跑压测,测试库与业务库严格隔离。
3.2 执行 prepare 生成测试数据
| |
sysbench 会创建 10 张结构一致的测试表(sbtest1 到 sbtest10),每张 100 万行。表结构如下:
| |
3.3 数据量的选择原则
数据量与 innodb_buffer_pool_size 的比例直接影响测试结果:
| 数据量 vs buffer pool | 测试侧重 |
|---|---|
| 数据量 < buffer pool | 大部分读命中内存,测 CPU 和内存带宽上限 |
| 数据量约等于 buffer pool 1.5 倍 | 兼顾内存命中和磁盘 I/O,贴近真实场景 |
| 数据量远大于 buffer pool | I/O 密集型,测存储性能瓶颈 |
建议:正式压测数据量至少为 buffer pool 的 1.5 倍,使测试包含一定比例的磁盘读取,避免全内存测试失真。
4. 第二阶段:实例预热(prewarm)
预热是容易被忽略但至关重要的环节。MySQL 重启或长时间空闲后,innodb_buffer_pool 是冷的,大量数据页不在内存中,直接压测会导致前几分钟大量磁盘 I/O,TPS 偏低,结果失真。
4.1 为什么必须预热
以本文真实案例为例(2 线程,60 秒压测),未做预热的实时数据如下:
[ 10s ] thds: 2 tps: 23.30 qps: 469.31 lat(95%): 272.27ms <- 冷启动,P95 高达 272ms
[ 20s ] thds: 2 tps: 77.70 qps: 1554.44 lat(95%): 56.84ms <- buffer pool 逐渐预热
[ 30s ] thds: 2 tps: 89.00 qps: 1780.01 lat(95%): 46.63ms <- 趋于稳定
[ 40s ] thds: 2 tps: 92.50 qps: 1849.98 lat(95%): 41.10ms <- 峰值
[ 50s ] thds: 2 tps: 88.40 qps: 1767.59 lat(95%): 46.63ms
[ 60s ] thds: 2 tps: 90.40 qps: 1807.20 lat(95%): 32.53ms
问题:最终汇总 TPS=76.84,但真实稳定态约 88-92 TPS,被第一个 10 秒的冷启动(TPS=23)严重拉低。汇总 P95=57.87ms,而稳定态 P95 只有 32-46ms,同样被冷启动的 272ms 拉高。
这正是需要预热的原因:让 buffer pool 充分加载热点数据后,再开始正式计时。
4.2 sysbench prewarm 子命令(推荐)
prewarm 是 sysbench 的内置子命令(与 prepare、run、cleanup 并列),会对测试表执行全表扫描,将数据页强制加载进 InnoDB buffer pool:
| |
prewarm不是参数,是和prepare/run/cleanup并列的子命令,写在命令最后。
4.3 完整四步流程
| |
4.4 预热后的效果验证
可通过 MySQL 查询 buffer pool 命中率确认预热效果,稳定在 99% 以上再开始压测:
| |
5. 第三阶段:压测执行(run)
5.1 压测命令
| |
5.2 关键参数说明
| 参数 | 说明 | 建议值 |
|---|---|---|
--threads | 并发线程数,模拟并发连接数 | 从 4 开始,逐步翻倍找拐点 |
--time | 压测持续时长(秒) | 不低于 300 秒,排除冷启动干扰 |
--report-interval | 实时输出间隔(秒) | 10,便于观察稳定态区间 |
--rand-type | 数据分布类型 | uniform(均匀)或 pareto(热点模拟) |
--table-size | 每张表的行数 | 根据 buffer pool 大小调整 |
--tables | 测试表数量 | 10,减少单表锁竞争 |
5.3 梯度测试找性能拐点
单次压测结果意义有限,应通过梯度递增线程数来找到系统的性能拐点:
| |
当 TPS 不再随线程数增长、P95 开始显著上升时,上一个线程数即为最优并发点。
6. 第四阶段:压测结果解析
6.1 真实案例
以下是一次真实压测的完整输出,2 线程、60 秒、oltp_read_write 场景,数据库端口 3307,测试库 base07,10 张表每张 10 万行:
压测命令:
| |
完整输出:
[ 10s ] thds: 2 tps: 23.30 qps: 469.31 (r/w/o: 328.94/93.58/46.79) lat(95%): 272.27 err/s: 0.00 reconn/s: 0.00
[ 20s ] thds: 2 tps: 77.70 qps: 1554.44 (r/w/o: 1087.83/311.21/155.40) lat(95%): 56.84 err/s: 0.00 reconn/s: 0.00
[ 30s ] thds: 2 tps: 89.00 qps: 1780.01 (r/w/o: 1246.01/356.00/178.00) lat(95%): 46.63 err/s: 0.00 reconn/s: 0.00
[ 40s ] thds: 2 tps: 92.50 qps: 1849.98 (r/w/o: 1294.98/370.00/185.00) lat(95%): 41.10 err/s: 0.00 reconn/s: 0.00
[ 50s ] thds: 2 tps: 88.40 qps: 1767.59 (r/w/o: 1237.60/353.20/176.80) lat(95%): 46.63 err/s: 0.00 reconn/s: 0.00
[ 60s ] thds: 2 tps: 90.40 qps: 1807.20 (r/w/o: 1264.80/361.60/180.80) lat(95%): 32.53 err/s: 0.00 reconn/s: 0.00
SQL statistics:
queries performed:
read: 64610
write: 18460
other: 9230
total: 92300
transactions: 4615 (76.84 per sec.)
queries: 92300 (1536.81 per sec.)
ignored errors: 0 (0.00 per sec.)
reconnects: 0 (0.00 per sec.)
General statistics:
total time: 60.0580s
total number of events: 4615
Latency (ms):
min: 5.61
avg: 26.01
max: 838.69
95th percentile: 57.87
sum: 120018.85
Threads fairness:
events (avg/stddev): 2307.5000/6.50
execution time (avg/stddev): 60.0094/0.01
6.2 实时输出逐行解读
| 时间点 | TPS | QPS | P95(ms) | 分析 |
|---|---|---|---|---|
| 10s | 23.30 | 469 | 272.27 | 冷启动:buffer pool 未预热,大量磁盘 I/O,延迟极高 |
| 20s | 77.70 | 1554 | 56.84 | buffer pool 逐渐加载热点页,TPS 快速回升 |
| 30s | 89.00 | 1780 | 46.63 | 趋于稳定,已进入热态 |
| 40s | 92.50 | 1849 | 41.10 | 峰值,buffer pool 命中率最高 |
| 50s | 88.40 | 1767 | 46.63 | 正常波动,稳定态 |
| 60s | 90.40 | 1807 | 32.53 | 持续稳定 |
核心结论:第 10 秒是冷启动噪声,真实稳定态 TPS 约为 88-92,稳定态 P95 约为 32-46ms。
6.3 三大核心指标详解
TPS(Transactions Per Second,每秒事务数)
transactions: 4615 (76.84 per sec.)
4615:60 秒内完成的总事务数76.84:全程平均 TPS,含冷启动的 10s,被严重拉低- 真实稳定态 TPS 约为 88-92(以 30-60s 区间为准)
TPS 的本质:一次事务从 BEGIN 到 COMMIT 算一个完整事务。oltp_read_write 每个事务默认包含约 20 条 SQL(10 次 point select + range/order/distinct select + update + delete + insert)。
QPS(Queries Per Second,每秒查询数)
queries: 92300 (1536.81 per sec.)
- 全程共执行 92300 条 SQL,平均每秒 1536.81 条
- 换算验证:
TPS x 每事务SQL数 = 76.84 x 20 = 1536.8,完全吻合 - read:write:other = 64610:18460:9230,约为 70%:20%:10%,符合
oltp_read_write的读写比设计
LAT(Latency,响应延迟)
Latency (ms):
min: 5.61 <- 最优单次响应(内存命中+无竞争)
avg: 26.01 <- 全程平均,含冷启动拉高
max: 838.69 <- 冷启动阶段的极端慢请求
95th percentile: 57.87 <- 全程 P95,含冷启动影响
含冷启动 vs 稳定态对比:
| 指标 | 全程汇总(含冷启动) | 稳定态(30-60s) |
|---|---|---|
| TPS | 76.84 | 88-92 |
| P95 | 57.87ms | 32-46ms |
| max | 838.69ms | 约 50-100ms |
max=838ms 分析:发生在第 10s 冷启动阶段,是 buffer pool 未加载时磁盘 I/O 阻塞导致的极端值,不代表系统稳定运行时的真实水平。若做了 prewarm 预热,max 值会大幅下降。
6.4 汇总报告逐段解读
SQL statistics:
queries performed:
read: 64610 # SELECT 类语句:point select、range select 等
write: 18460 # INSERT / UPDATE / DELETE
other: 9230 # BEGIN / COMMIT 等控制语句
total: 92300
read:write 约为 3.5:1,是 oltp_read_write 的典型比例(读密集型混合负载)。
transactions: 4615 (76.84 per sec.) # TPS
queries: 92300 (1536.81 per sec.) # QPS
ignored errors: 0 (0.00 per sec.) # 无报错,连接正常
reconnects: 0 (0.00 per sec.) # 无重连,网络稳定
General statistics:
total time: 60.0580s # 实际运行时长(略超 60s 属正常)
total number of events: 4615 # 总事务数
Latency (ms):
min: 5.61 # 最快的单次事务
avg: 26.01 # 平均响应时间(受冷启动拉高)
max: 838.69 # 最慢的单次事务(冷启动阶段)
95th: 57.87 # P95:95% 的事务在 57.87ms 内完成
sum: 120018.85 # 所有事务响应时间之和(用于计算 avg)
Threads fairness:
events (avg/stddev): 2307.5 / 6.50 # 每线程处理事务数均值 / 标准差
execution time (avg/stddev): 60.0094 / 0.01 # 每线程运行时间均值 / 标准差
Threads fairness 解读:
- 2 个线程分别处理了约 2307.5 次事务,标准差仅 6.5(偏差率 0.28%),说明负载极为均衡
- 两个线程运行时间几乎完全一致(标准差 0.01s),没有线程饥饿或阻塞问题
- stddev 越小越好,若 stddev 很大说明存在热点锁竞争或资源分配不均
6.5 本次压测结论
| 项目 | 数值 | 评估 |
|---|---|---|
| 稳定态 TPS | 88-92 | 2 线程下的吞吐上限 |
| 稳定态 P95 | 32-46ms | 延迟可接受 |
| 冷启动 P95 | 272ms | 预热前延迟极高,需做 prewarm |
| 线程负载均衡 | stddev=6.5 | 优秀,无竞争热点 |
| 错误/重连 | 0 | 连接稳定 |
优化建议:本次未做 prewarm 预热,导致汇总 TPS(76.84)与稳定态 TPS(88-92)相差约 15%,汇总 P95(57.87ms)比稳定态 P95(32-46ms)高约 25%。正式压测前应先执行 prewarm 子命令,再运行 run,确保结果真实反映系统热态性能。
7. 清理
| |
8. 常见误区
- 不做预热直接压测:冷启动阶段 TPS 和延迟数据严重失真,应先执行
prewarm子命令 - 压测时间太短:60 秒含冷启动后实际稳定窗口不足,建议不低于 300 秒
- 只看汇总 TPS 不看实时数据:汇总 TPS 被冷启动拉低,应结合实时输出识别稳定态区间
- 只看 TPS 不看 P95:高 TPS 配合高 P95 在生产中不可接受,二者须同时满足 SLA
- 被测 MySQL 与 sysbench 同机运行:二者争抢资源,结果失真,应分机测试
9. 小结
完整的 sysbench 压测分四个阶段:
- 数据准备(prepare):生成合理大小的测试数据,建议数据量为 buffer pool 的 1.5 倍以上
- 实例预热(prewarm):使用 sysbench 内置
prewarm子命令全表扫描,将热点页加载进 buffer pool,避免冷启动噪声污染结果 - 压测执行(run):梯度递增线程数找拐点,
--time不低于 300 秒,以稳定阶段实时数据为准 - 结果解析:TPS 看吞吐,QPS 看总 SQL 量,P95 是最核心的 SLA 指标;结合实时输出区分冷启动区间与稳定态,配合系统监控定位瓶颈