MySQL 性能压测:Sysbench 1.0 实战原创
# MySQL 性能压测:Sysbench 1.0 实战
# 1. 网上大量教程还停在 sysbench 0.5
搜「sysbench 压测 MySQL」,很容易抄到这样一段安装步骤:
git clone https://github.com/akopytov/sysbench.git
git checkout 0.5 # 这里就错了
./autogen.sh && ./configure && make -j && make install
2
3
照着做会掉进一连串对不上的坑:脚本目录不是 /usr/local/share/sysbench,命令语法也不是 --test=/path/to/oltp.lua。sysbench 从 1.0 开始做了不兼容的重构,0.5 时代的用法几乎全部失效:
| 0.5(已淘汰) | 1.0+(现行) | |
|---|---|---|
| 指定测试 | --test=/usr/local/share/sysbench/oltp.lua | 直接写测试名 oltp_read_write |
| 测试脚本 | oltp.lua、parallel_prepare.lua | oltp_read_write.lua、oltp_read_only.lua 等一组 |
| 脚本路径 | /usr/local/share/sysbench | /usr/share/sysbench |
| 线程数 | --num-threads | --threads |
| 压测时长 | --max-time | --time |
| 请求总数 | --max-requests | --events |
| 表数量 | --oltp-tables-count | --tables |
| 表行数 | --oltp-table-size | --table-size |
所以看到教程里出现 --num-threads 或 --test=,基本可以判定它是 0.5 时代的内容,别再往下抄。
# 2. 压测的意义在于「可比较」
跑一次压测拿到一个 QPS 数字,本身没有价值。压测真正的用处是在两个变量之间做对比:换了参数前后、换了硬件前后、升级版本前后。
要让对比成立,有三个前提必须守住:
- 数据量要超出缓冲池。如果测试表能被
innodb_buffer_pool_size全部装下,测的就是内存性能,改任何磁盘相关参数都看不出差别。 - 要有预热。InnoDB 冷启动时缓冲池是空的,前几十秒的数据严重偏低,必须丢弃。
- 压测机与数据库分离。sysbench 本身吃 CPU,和 mysqld 抢资源会让结果失真,尤其在高并发档位。
另外要清楚 sysbench 测的是通用 OLTP 模型,不是你的业务。它适合回答「这台机器/这组参数的相对能力如何」,不适合回答「我的业务能扛多少 QPS」——后者只能用贴近真实的 SQL 去测。
# 3. 安装与完整压测流程
# 3.1 安装
优先用包管理器,别再从源码编译:
# RHEL / CentOS / Rocky
curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.rpm.sh | sudo bash
sudo yum -y install sysbench
# Debian / Ubuntu
curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.deb.sh | sudo bash
sudo apt -y install sysbench
2
3
4
5
6
7
确认版本,必须是 1.0 以上:
sysbench --version
预期输出:
sysbench 1.0.20
看看有哪些内置测试可用:
ls /usr/share/sysbench/*.lua
预期输出:
/usr/share/sysbench/bulk_insert.lua /usr/share/sysbench/oltp_read_write.lua
/usr/share/sysbench/oltp_delete.lua /usr/share/sysbench/oltp_update_index.lua
/usr/share/sysbench/oltp_insert.lua /usr/share/sysbench/oltp_update_non_index.lua
/usr/share/sysbench/oltp_point_select.lua /usr/share/sysbench/oltp_write_only.lua
/usr/share/sysbench/oltp_read_only.lua /usr/share/sysbench/select_random_points.lua
2
3
4
5
# 3.2 准备测试库与账号
CREATE DATABASE sbtest;
CREATE USER 'sbtest'@'%' IDENTIFIED BY '<password>';
GRANT ALL PRIVILEGES ON sbtest.* TO 'sbtest'@'%';
FLUSH PRIVILEGES;
2
3
4
把连接参数抽成变量,避免每条命令重复一长串:
SB_CONN="--mysql-host=192.0.2.10 --mysql-port=3306 \
--mysql-user=sbtest --mysql-password=<password> --mysql-db=sbtest"
2
# 3.3 三个阶段:prepare / run / cleanup
sysbench 的每个测试都遵循这三步。
准备数据——--tables × --table-size 决定数据量。10 张表 × 100 万行大约 2.5GB,务必确保它大于缓冲池:
sysbench oltp_read_write $SB_CONN \
--tables=10 --table-size=1000000 \
--threads=8 \
prepare
2
3
4
执行压测:
sysbench oltp_read_write $SB_CONN \
--tables=10 --table-size=1000000 \
--threads=64 \
--time=300 \
--warmup-time=60 \
--report-interval=10 \
--rand-type=uniform \
--db-ps-mode=disable \
run
2
3
4
5
6
7
8
9
关键参数:
--warmup-time=60:预热 60 秒且不计入统计,这是 1.0 才有的参数,比手工丢弃前几十秒可靠--report-interval=10:每 10 秒打一行中间结果,用来观察是否平稳;抖动大说明有检查点或刷脏干扰--rand-type=uniform:默认是special(热点集中),会让缓存命中率虚高。想测真实磁盘能力就用uniform--db-ps-mode=disable:关闭预处理语句,更接近多数应用的行为--time=300:至少 5 分钟,太短测不到检查点行为
清理:
sysbench oltp_read_write $SB_CONN --tables=10 cleanup
# 3.4 读懂结果
预期输出(节选):
SQL statistics:
queries performed:
read: 1120504
write: 320144
other: 160072
total: 1600720
transactions: 80036 (266.75 per sec.)
queries: 1600720 (5335.02 per sec.)
Latency (ms):
min: 2.14
avg: 59.87
max: 892.31
95th percentile: 142.39
sum: 4791024.55
Threads fairness:
events (avg/stddev): 1250.5625/12.35
execution time (avg/stddev): 299.4390/0.11
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
看结果的顺序建议是:
- 95th percentile 优先于 avg。平均延迟会被大量快请求拉低,真正影响用户体验的是长尾。
- max 与 95th 的差距。差一个数量级通常意味着有周期性停顿——检查点、刷脏页、或 binlog 落盘。
- transactions per sec 才是 OLTP 场景的核心指标,queries per sec 会随测试模型的语句构成变化而变化,跨模型不可比。
- Threads fairness 的 stddev。偏大说明线程间调度不均,可能是压测机资源不足。
# 3.5 做成可比较的对比测试
单点数字没意义,要跑一组。典型做法是固定其他条件、只变并发数:
for t in 8 16 32 64 128 256; do
echo "=== threads=$t ==="
sysbench oltp_read_write $SB_CONN \
--tables=10 --table-size=1000000 \
--threads=$t --time=180 --warmup-time=30 \
--rand-type=uniform --db-ps-mode=disable \
run | grep -E "transactions:|95th percentile:"
done
2
3
4
5
6
7
8
把结果画成「并发数 → TPS」曲线,能看到明显的拐点:TPS 到顶后不再上升、而延迟继续攀升的那个并发数,就是这套配置的实际承载上限。超过该点继续加并发只会恶化延迟。
# 3.6 常用测试模型的取舍
| 测试 | 读写比 | 用途 |
|---|---|---|
oltp_read_only | 纯读 | 测缓冲池与 CPU 上限 |
oltp_write_only | 纯写 | 测 redo/binlog 落盘与 IO 能力 |
oltp_read_write | 约 7:3 | 综合场景,最常用 |
oltp_point_select | 纯主键点查 | 测极限 QPS 与网络往返开销 |
bulk_insert | 批量插入 | 测导入吞吐 |
调 innodb_flush_log_at_trx_commit、sync_binlog 这类持久性参数时,用 oltp_write_only 差异最明显;调缓冲池大小则用 oltp_read_only。相关参数见MySQL8 配置文件 my.cnf 重要参数解读。
# 4. 四个高频坑
# 4.1 抄了 0.5 的命令,报 unknown option
症状:sysbench: unrecognized option '--num-threads',或 --test=... 提示找不到文件。
原因:这些都是 0.5 的参数名,1.0 已重命名(见第 1 节对照表)。
解药:按对照表逐个替换,测试名直接写 oltp_read_write,不要写 .lua 路径。
# 4.2 测出来的数字高得不真实
症状:几十万 QPS,改什么参数都不变。
原因:两种可能——测试数据集小于缓冲池,全部命中内存;或者用了默认的 --rand-type=special,访问高度集中在热点区。
解药:把 --tables × --table-size 放大到缓冲池的 2 倍以上,并显式指定 --rand-type=uniform。
# 4.3 prepare 阶段极慢
症状:准备 1000 万行数据要跑几十分钟。
原因:prepare 默认单线程建数据。
解药:给 prepare 也加并发,--threads 设为表数量即可(每张表一个线程):
sysbench oltp_read_write $SB_CONN --tables=10 --table-size=1000000 --threads=10 prepare
注意并发数超过表数量不会更快,因为 sysbench 按表切分任务。
# 4.4 结果每次跑都差很多
症状:同样的命令,两次 TPS 相差 30% 以上。
原因:常见于三点——没有预热、压测时长太短没跨过检查点、或压测机与数据库同机互抢资源。
解药:固定 --warmup-time=60 --time=300,压测端独立部署,并且每轮之间让数据库空转一会儿再开始下一轮。测完记得对比 --report-interval 打出的中间行是否平稳,不平稳的结果不要采信。
# 5. 可复用要点
- 先确认版本再抄命令。出现
--num-threads/--test=就是 0.5 的过时内容,1.0 的参数名完全不同。 - 数据量必须大于缓冲池,否则测的是内存不是数据库。
- 看 95 分位而不是平均值,并关注 max 与 95 分位的差距来发现周期性停顿。
- 压测的产出是曲线不是数字。跑一组并发档位找到 TPS 拐点,才能说明系统的实际承载上限。
- 按调优目标选测试模型:调持久性参数用
oltp_write_only,调缓冲池用oltp_read_only。
# 6. 延伸阅读
- MySQL8 配置文件 my.cnf 重要参数解读——压测时最常调整的那些参数
- MySQL执行计划——单条 SQL 慢的分析方法
- MySQL8设置slowlog记录所有语句——压测期间抓取全量 SQL
{
"topic": "使用 Sysbench 1.0 对 MySQL 做性能压测",
"tool": {"name": "sysbench", "required_version": ">=1.0", "obsolete_version": "0.5"},
"option_migration_0_5_to_1_0": {
"--test=<path.lua>": "直接写测试名,如 oltp_read_write",
"--num-threads": "--threads",
"--max-time": "--time",
"--max-requests": "--events",
"--oltp-tables-count": "--tables",
"--oltp-table-size": "--table-size",
"script_dir": {"old": "/usr/local/share/sysbench", "new": "/usr/share/sysbench"}
},
"install": {
"rpm": "curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.rpm.sh | sudo bash && sudo yum -y install sysbench",
"deb": "curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.deb.sh | sudo bash && sudo apt -y install sysbench"
},
"phases": ["prepare", "run", "cleanup"],
"recommended_run_flags": {
"--threads": "按档位扫描 8/16/32/64/128/256",
"--time": 300,
"--warmup-time": 60,
"--report-interval": 10,
"--rand-type": "uniform",
"--db-ps-mode": "disable"
},
"validity_preconditions": [
"数据量(tables × table-size)需大于 innodb_buffer_pool_size 的 2 倍",
"必须预热并丢弃预热期数据",
"压测端与数据库实例分机部署"
],
"test_models": {
"oltp_read_only": "测缓冲池与 CPU 上限",
"oltp_write_only": "测 redo/binlog 落盘与 IO,调持久性参数时用",
"oltp_read_write": "约 7:3 综合场景,最常用",
"oltp_point_select": "测极限 QPS 与网络往返",
"bulk_insert": "测导入吞吐"
},
"result_reading_order": [
"95th percentile 优先于 avg",
"max 与 95th 差一个数量级 => 存在周期性停顿(检查点/刷脏/binlog 落盘)",
"transactions per sec 为 OLTP 核心指标,queries per sec 跨模型不可比",
"Threads fairness stddev 偏大 => 压测机资源不足"
],
"pitfalls": [
{"symptom": "unrecognized option --num-threads", "cause": "抄了 0.5 的参数名", "fix": "按迁移表替换为 1.0 参数"},
{"symptom": "QPS 高得不真实且调参无变化", "cause": "数据集小于缓冲池,或使用默认 rand-type=special 热点集中", "fix": "放大数据量并指定 --rand-type=uniform"},
{"symptom": "prepare 极慢", "cause": "默认单线程建数据", "fix": "prepare 阶段加 --threads,设为表数量"},
{"symptom": "多次结果波动超过 30%", "cause": "无预热 / 时长过短 / 压测端与数据库同机", "fix": "固定 warmup-time 与 time,压测端独立部署"}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
- 02
- ORDER BY 配合 LIMIT 触发的索引选择陷阱 原创08-07
- 03
- Nginx 运维知识地图:从配置基础到反向代理实战 原创07-29