1. 基准测试的边界认知

在使用 sysbench 之前,必须先明确基准测试能回答什么、不能回答什么,否则很容易得出错误结论。

能回答的问题:

  • 当前硬件和配置下,MySQL 的 TPS/QPS 上限是多少
  • 两套配置方案之间的相对性能差异(如调整 innodb_buffer_pool_size 前后)
  • 某次参数调整或版本升级后,性能是否有可量化的提升或退步
  • 目标并发量下,响应延迟是否在可接受范围内

不能回答的问题:

  • 生产环境的真实性能(基准负载是理想化的,生产 SQL 复杂得多)
  • 长期运行稳定性(基准测试通常持续分钟级)
  • 热点、锁竞争、主从延迟等业务相关的性能问题

sysbench 的结果是性能的参考基线,而非生产能力的直接映射。同一套硬件,sysbench 跑出 5 万 TPS,生产复杂查询下可能只有几千 QPS。


2. 安装

1
2
3
4
5
6
7
8
9
# 添加 Percona yum 源
curl -sSL https://repo.percona.com/yum/percona-release-latest.noarch.rpm -o percona-release.rpm
rpm -ivh percona-release.rpm

# 安装 sysbench
yum install -y sysbench

# 验证版本
sysbench --version

安装后路径:主程序 /usr/bin/sysbench,测试脚本 /usr/share/sysbench/


3. 第一阶段:数据准备(prepare)

3.1 创建测试库与专用账号

1
2
3
4
5
CREATE DATABASE sbtest CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

CREATE USER 'sbtest'@'%' IDENTIFIED BY 'SbTest@2026';
GRANT ALL PRIVILEGES ON sbtest.* TO 'sbtest'@'%';
FLUSH PRIVILEGES;

不要用 root 账号跑压测,测试库与业务库严格隔离。

3.2 执行 prepare 生成测试数据

1
2
3
4
5
6
7
8
9
sysbench oltp_read_write \
  --mysql-host=127.0.0.1 \
  --mysql-port=3306 \
  --mysql-user=sbtest \
  --mysql-password=SbTest@2026 \
  --mysql-db=sbtest \
  --tables=10 \
  --table-size=1000000 \
  prepare

sysbench 会创建 10 张结构一致的测试表(sbtest1 到 sbtest10),每张 100 万行。表结构如下:

1
2
3
4
5
6
7
8
CREATE TABLE sbtest1 (
  id   INT        NOT NULL AUTO_INCREMENT,
  k    INT        NOT NULL DEFAULT 0,
  c    CHAR(120)  NOT NULL DEFAULT '',
  pad  CHAR(60)   NOT NULL DEFAULT '',
  PRIMARY KEY (id),
  KEY k_1 (k)
) ENGINE=InnoDB;

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 的内置子命令(与 prepareruncleanup 并列),会对测试表执行全表扫描,将数据页强制加载进 InnoDB buffer pool:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
sysbench oltp_read_write \
  --db-driver=mysql \
  --mysql-host=127.0.0.1 \
  --mysql-port=3307 \
  --mysql-user=root \
  --mysql-password=333333 \
  --mysql-db=base07 \
  --table-size=100000 \
  --tables=10 \
  prewarm

prewarm 不是参数,是和 prepare / run / cleanup 并列的子命令,写在命令最后。

4.3 完整四步流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 第一步:生成测试数据
sysbench oltp_read_write ... prepare

# 第二步:预热 buffer pool
sysbench oltp_read_write ... prewarm

# 第三步:执行压测
sysbench oltp_read_write ... run

# 第四步:清理测试数据
sysbench oltp_read_write ... cleanup

4.4 预热后的效果验证

可通过 MySQL 查询 buffer pool 命中率确认预热效果,稳定在 99% 以上再开始压测:

1
2
3
4
5
6
7
SELECT
  FORMAT((1 - a.v / b.v) * 100, 2) AS hit_rate_pct
FROM
  (SELECT VARIABLE_VALUE v FROM performance_schema.global_status
   WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') a,
  (SELECT VARIABLE_VALUE v FROM performance_schema.global_status
   WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests') b;

5. 第三阶段:压测执行(run)

5.1 压测命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
sysbench oltp_read_write \
  --db-driver=mysql \
  --mysql-host=127.0.0.1 \
  --mysql-port=3306 \
  --mysql-user=sbtest \
  --mysql-password=SbTest@2026 \
  --mysql-db=sbtest \
  --tables=10 \
  --table-size=1000000 \
  --threads=16 \
  --time=300 \
  --report-interval=10 \
  run

5.2 关键参数说明

参数 说明 建议值
--threads 并发线程数,模拟并发连接数 从 4 开始,逐步翻倍找拐点
--time 压测持续时长(秒) 不低于 300 秒,排除冷启动干扰
--report-interval 实时输出间隔(秒) 10,便于观察稳定态区间
--rand-type 数据分布类型 uniform(均匀)或 pareto(热点模拟)
--table-size 每张表的行数 根据 buffer pool 大小调整
--tables 测试表数量 10,减少单表锁竞争

5.3 梯度测试找性能拐点

单次压测结果意义有限,应通过梯度递增线程数来找到系统的性能拐点:

1
2
3
4
5
6
7
for threads in 4 8 16 32 64; do
  sysbench oltp_read_write \
    --threads=$threads \
    --time=120 \
    --report-interval=10 \
    ... run 2>&1 | grep -E "tps|transactions|95th"
done

当 TPS 不再随线程数增长、P95 开始显著上升时,上一个线程数即为最优并发点。


6. 第四阶段:压测结果解析

6.1 真实案例

以下是一次真实压测的完整输出,2 线程、60 秒、oltp_read_write 场景,数据库端口 3307,测试库 base07,10 张表每张 10 万行:

压测命令:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
sysbench oltp_read_write \
  --db-driver=mysql \
  --mysql-host=127.0.0.1 \
  --mysql-port=3307 \
  --mysql-user=root \
  --mysql-password=333333 \
  --mysql-db=base07 \
  --table-size=100000 \
  --tables=10 \
  --threads=2 \
  --time=60 \
  --report-interval=10 \
  run

完整输出:

[ 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 的本质:一次事务从 BEGINCOMMIT 算一个完整事务。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. 清理

1
2
3
4
5
6
7
8
9
sysbench oltp_read_write \
  --db-driver=mysql \
  --mysql-host=127.0.0.1 \
  --mysql-port=3307 \
  --mysql-user=root \
  --mysql-password=333333 \
  --mysql-db=base07 \
  --tables=10 \
  cleanup

8. 常见误区

  • 不做预热直接压测:冷启动阶段 TPS 和延迟数据严重失真,应先执行 prewarm 子命令
  • 压测时间太短:60 秒含冷启动后实际稳定窗口不足,建议不低于 300 秒
  • 只看汇总 TPS 不看实时数据:汇总 TPS 被冷启动拉低,应结合实时输出识别稳定态区间
  • 只看 TPS 不看 P95:高 TPS 配合高 P95 在生产中不可接受,二者须同时满足 SLA
  • 被测 MySQL 与 sysbench 同机运行:二者争抢资源,结果失真,应分机测试

9. 小结

完整的 sysbench 压测分四个阶段:

  1. 数据准备(prepare):生成合理大小的测试数据,建议数据量为 buffer pool 的 1.5 倍以上
  2. 实例预热(prewarm):使用 sysbench 内置 prewarm 子命令全表扫描,将热点页加载进 buffer pool,避免冷启动噪声污染结果
  3. 压测执行(run):梯度递增线程数找拐点,--time 不低于 300 秒,以稳定阶段实时数据为准
  4. 结果解析:TPS 看吞吐,QPS 看总 SQL 量,P95 是最核心的 SLA 指标;结合实时输出区分冷启动区间与稳定态,配合系统监控定位瓶颈