强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Sysbench 完全指南 / 第四章:OLTP 标准测试

第四章:OLTP 标准测试

4.1 概述

Sysbench 内置了多个 OLTP(Online Transaction Processing,在线事务处理)标准测试脚本,这些脚本模拟了典型的数据库工作负载。OLTP 测试是 Sysbench 最核心的功能,广泛用于数据库性能评估和调优。

4.1.1 可用的 OLTP 测试

测试名称操作类型说明
oltp_read_write读写混合最常用,模拟典型 OLTP 负载(75% 读 + 25% 写)
oltp_read_only只读模拟读密集场景(报表、分析)
oltp_write_only只写模拟写密集场景(日志写入、数据导入)
oltp_point_select点查询SELECT WHERE id=?,测试索引查询性能
oltp_range_select范围查询SELECT WHERE id BETWEEN ? AND ?
oltp_insert插入INSERT 操作
oltp_update_index索引更新UPDATE WHERE id=?(更新索引列)
oltp_update_non_index非索引更新UPDATE WHERE id=?(更新非索引列)
oltp_delete删除DELETE WHERE id=?
oltp_begin_commit事务提交BEGIN + COMMIT,测试事务开销
oltp_begin_rollback事务回滚BEGIN + ROLLBACK,测试回滚开销

4.2 oltp_read_write 详解

oltp_read_write 是最常用的 OLTP 测试,它在一个事务中混合了多种操作。

4.2.1 事务结构

每个事务包含以下操作(按顺序执行):

步骤操作SQL 示例
1BEGINBEGIN
2点查询SELECT c FROM sbtest1 WHERE id = ?
3范围查询SELECT c FROM sbtest1 WHERE id BETWEEN ? AND ?
4范围 SUMSELECT SUM(k) FROM sbtest1 WHERE id BETWEEN ? AND ?
5范围 ORDER BYSELECT c FROM sbtest1 WHERE id BETWEEN ? AND ? ORDER BY c
6范围 DISTINCTSELECT DISTINCT c FROM sbtest1 WHERE id BETWEEN ? AND ? ORDER BY c
7索引更新UPDATE sbtest1 SET k = k + 1 WHERE id = ?
8非索引更新UPDATE sbtest1 SET c = ? WHERE id = ?
9删除DELETE FROM sbtest1 WHERE id = ?
10插入INSERT INTO sbtest1 VALUES (?, ?, ?, ?)
11COMMITCOMMIT

4.2.2 表结构

Sysbench 自动创建名为 sbtest1sbtest2 … 的表,结构如下:

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 (k)
) ENGINE=InnoDB;
字段类型说明
idINT (PK, AUTO_INCREMENT)主键
kINT索引列,用于更新操作
cCHAR(120)120 字符随机字符串
padCHAR(60)60 字符随机字符串

4.2.3 基本用法

# 1. 准备数据(创建 16 张表,每张表 100 万行)
sysbench oltp_read_write \
  --mysql-host=127.0.0.1 \
  --mysql-port=3306 \
  --mysql-user=root \
  --mysql-password=secret \
  --mysql-db=sbtest \
  --tables=16 \
  --table-size=1000000 \
  prepare

# 2. 运行测试
sysbench oltp_read_write \
  --mysql-host=127.0.0.1 \
  --mysql-port=3306 \
  --mysql-user=root \
  --mysql-password=secret \
  --mysql-db=sbtest \
  --tables=16 \
  --table-size=1000000 \
  --threads=16 \
  --time=300 \
  --histogram \
  --percentile=99 \
  run

# 3. 清理
sysbench oltp_read_write \
  --mysql-host=127.0.0.1 \
  --mysql-user=root \
  --mysql-password=secret \
  --mysql-db=sbtest \
  --tables=16 \
  cleanup

4.2.4 输出示例

SQL statistics:
    queries performed:
        read:                            456789   ← 读查询总数
        write:                           130567   ← 写查询总数
        other:                           65234    ← 其他查询(BEGIN/COMMIT)
        total:                           652590   ← 总查询数
    transactions:                         32617   (1087.23 per sec.)   ← 事务总数 (TPS)
    queries:                              652590  (21754.60 per sec.)  ← 查询总数 (QPS)
    ignored errors:                       0       (0.00 per sec.)
    reconnects:                           0       (0.00 per sec.)

General statistics:
    total time:                          30.0034s
    total number of events:              32617

Latency (ms):
         min:                                    2.89
         avg:                                   14.71
         max:                                  128.45
         95th percentile:                       22.69
         sum:                               479812.34

Threads fairness:
    events (avg/stddev):           2038.5625/12.34
    execution time (avg/stddev):   29.9883/0.01

4.2.5 关键指标解读

指标含义示例值
transactions/sec每秒事务数(TPS)1087.23
queries/sec每秒查询数(QPS)21754.60
read / write / other读/写/其他操作比例70% / 20% / 10%
95th percentileP95 延迟22.69 ms
avg latency平均延迟14.71 ms

4.3 oltp_read_only 详解

oltp_read_only 只包含读操作,适用于评估只读工作负载下的数据库性能。

4.3.1 事务结构

步骤操作SQL
1BEGINBEGIN
2点查询SELECT c FROM sbtest WHERE id = ?
3范围查询SELECT c FROM sbtest WHERE id BETWEEN ? AND ?
4范围 SUMSELECT SUM(k) FROM sbtest WHERE id BETWEEN ? AND ?
5范围 ORDER BYSELECT c FROM sbtest WHERE id BETWEEN ? AND ? ORDER BY c
6范围 DISTINCTSELECT DISTINCT c FROM sbtest WHERE id BETWEEN ? AND ? ORDER BY c
7COMMITCOMMIT

4.3.2 用法

# 准备数据(如果还未准备)
sysbench oltp_read_only \
  --mysql-host=127.0.0.1 \
  --mysql-user=root \
  --mysql-password=secret \
  --tables=16 \
  --table-size=1000000 \
  prepare

# 运行只读测试
sysbench oltp_read_only \
  --mysql-host=127.0.0.1 \
  --mysql-user=root \
  --mysql-password=secret \
  --tables=16 \
  --table-size=1000000 \
  --threads=32 \
  --time=300 \
  --histogram \
  run

4.3.3 适用场景

场景说明
读写分离测试评估只读从库的处理能力
缓存命中率测试数据量 < Buffer Pool 时,几乎全缓存命中
查询优化验证验证索引优化、SQL 优化的效果
只读副本评估云数据库只读副本的性能基准

4.4 其他 OLTP 测试

4.4.1 oltp_write_only

只包含写操作,评估纯写入性能:

sysbench oltp_write_only \
  --mysql-host=127.0.0.1 \
  --mysql-user=root \
  --mysql-password=secret \
  --tables=16 \
  --table-size=1000000 \
  --threads=16 \
  --time=300 \
  run

事务结构:BEGIN → DELETE → INSERT → UPDATE(索引) → UPDATE(非索引) → COMMIT

4.4.2 oltp_point_select

最简单的点查询测试,适合评估索引查询的极限性能:

sysbench oltp_point_select \
  --mysql-host=127.0.0.1 \
  --mysql-user=root \
  --mysql-password=secret \
  --tables=16 \
  --table-size=1000000 \
  --threads=64 \
  --time=60 \
  run

每条操作:SELECT c FROM sbtest WHERE id = ?

提示oltp_point_select 通常能达到很高的 QPS(数万到数十万),适合作为数据库响应能力的极端基准。

4.4.3 oltp_update_index vs oltp_update_non_index

# 更新索引列 k(更慢,因为需要更新索引)
sysbench oltp_update_index \
  --mysql-host=127.0.0.1 \
  --mysql-user=root \
  --tables=16 \
  --table-size=1000000 \
  --threads=16 \
  --time=60 \
  run

# 更新非索引列 c(更快,不需要更新索引)
sysbench oltp_update_non_index \
  --mysql-host=127.0.0.1 \
  --mysql-user=root \
  --tables=16 \
  --table-size=1000000 \
  --threads=16 \
  --time=60 \
  run

对比意义:两者的性能差异反映了索引维护的开销。

4.4.4 oltp_insert

sysbench oltp_insert \
  --mysql-host=127.0.0.1 \
  --mysql-user=root \
  --mysql-password=secret \
  --tables=16 \
  --threads=32 \
  --time=300 \
  run

适用场景:

  • 评估 INSERT 性能上限
  • 测试自增主键的竞争(auto-increment lock contention)
  • 对比不同存储引擎的写入能力

4.4.5 oltp_begin_commit 与 oltp_begin_rollback

这两个测试纯粹测量事务开销:

# 测量 BEGIN + COMMIT 开销
sysbench oltp_begin_commit \
  --mysql-host=127.0.0.1 \
  --mysql-user=root \
  --threads=32 \
  --time=60 \
  run

# 测量 BEGIN + ROLLBACK 开销
sysbench oltp_begin_rollback \
  --mysql-host=127.0.0.1 \
  --mysql-user=root \
  --threads=32 \
  --time=60 \
  run

4.5 OLTP 通用选项

4.5.1 表与数据选项

选项默认值说明
--tables=N1测试表数量
--table-size=N10000每张表的行数
--table-engine=STRINGinnodb存储引擎(innodb / myisam / tokudb 等)
--auto-inc[=on/off]on是否使用自增主键
--create-secondary[=on/off]on是否创建 k 列的索引
--create-table-options=STRING-额外的建表选项
# 创建 32 张表,每张 500 万行,使用 InnoDB
sysbench oltp_read_write \
  --mysql-host=127.0.0.1 \
  --mysql-user=root \
  --tables=32 \
  --table-size=5000000 \
  --table-engine=innodb \
  prepare

# 禁用二级索引(测试无索引场景)
sysbench oltp_read_write \
  --create-secondary=off \
  --tables=16 \
  --table-size=1000000 \
  prepare

# 额外的建表选项
sysbench oltp_read_write \
  --create-table-options="ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8" \
  --tables=16 \
  prepare

4.5.2 查询选项

选项默认值说明
--range-size=N100范围查询的范围大小
--point-selects=N10每个事务中的点查询数量
--simple-ranges=N1每个事务中的简单范围查询数量
--sum-ranges=N1每个事务中的 SUM 范围查询数量
--order-ranges=N1每个事务中的 ORDER BY 范围查询数量
--distinct-ranges=N1每个事务中的 DISTINCT 范围查询数量
--index-updates=N1每个事务中的索引更新数量
--non-index-updates=N1每个事务中的非索引更新数量
--delete-inserts=N1每个事务中的删除-插入对数量
--skip-trx[=on/off]off跳过 BEGIN/COMMIT(自动提交模式)
--secondary[=on/off]off使用二级索引作为辅助索引查找
--create-secondary[=on/off]on是否创建辅助索引
# 自定义读写比例
# 增加点查询数量,减少写操作
sysbench oltp_read_write \
  --mysql-host=127.0.0.1 \
  --mysql-user=root \
  --point-selects=20 \
  --simple-ranges=5 \
  --sum-ranges=5 \
  --order-ranges=5 \
  --distinct-ranges=5 \
  --index-updates=1 \
  --non-index-updates=1 \
  --delete-inserts=1 \
  --tables=16 \
  --table-size=1000000 \
  --threads=16 \
  --time=300 \
  run

# 自动提交模式(不使用事务)
sysbench oltp_read_write \
  --skip-trx=on \
  --mysql-host=127.0.0.1 \
  --mysql-user=root \
  --tables=16 \
  --table-size=1000000 \
  --threads=16 \
  --time=60 \
  run

4.6 综合测试方案

4.6.1 全面性能评估方案

#!/bin/bash
# full_oltp_benchmark.sh - 全面 OLTP 性能评估

MYSQL_HOST="127.0.0.1"
MYSQL_USER="root"
MYSQL_PASS="secret"
TABLES=16
TABLE_SIZE=1000000
DURATION=300
RESULT_DIR="./results_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$RESULT_DIR"

# 测试列表
TESTS="oltp_read_write oltp_read_only oltp_write_only oltp_point_select oltp_update_index oltp_update_non_index oltp_insert"

# 准备数据
echo ">>> Preparing data..."
sysbench oltp_read_write \
  --mysql-host=$MYSQL_HOST --mysql-user=$MYSQL_USER --mysql-password=$MYSQL_PASS \
  --tables=$TABLES --table-size=$TABLE_SIZE prepare

# 预热
echo ">>> Warming up..."
sysbench oltp_read_write \
  --mysql-host=$MYSQL_HOST --mysql-user=$MYSQL_USER --mysql-password=$MYSQL_PASS \
  --tables=$TABLES --table-size=$TABLE_SIZE \
  --threads=8 --time=60 run > /dev/null 2>&1

# 运行各种测试
for test in $TESTS; do
  for threads in 1 4 8 16 32 64; do
    echo ">>> $test with $threads threads"
    sysbench $test \
      --mysql-host=$MYSQL_HOST --mysql-user=$MYSQL_USER --mysql-password=$MYSQL_PASS \
      --tables=$TABLES --table-size=$TABLE_SIZE \
      --threads=$threads --time=$DURATION \
      --percentile=99 --histogram \
      --json="$RESULT_DIR/${test}_t${threads}.json" \
      run 2>&1 | tee "$RESULT_DIR/${test}_t${threads}.txt"
    
    sleep 10
  done
done

# 清理
sysbench oltp_read_write \
  --mysql-host=$MYSQL_HOST --mysql-user=$MYSQL_USER --mysql-password=$MYSQL_PASS \
  --tables=$TABLES cleanup

echo "Results saved to $RESULT_DIR"

4.6.2 渐进式压力测试

#!/bin/bash
# progressive_load.sh - 渐进式加压测试

for threads in 1 2 4 8 16 32 64 128 256; do
  echo "============================================"
  echo "Threads: $threads"
  echo "============================================"
  sysbench oltp_read_write \
    --mysql-host=127.0.0.1 \
    --mysql-user=root \
    --mysql-password=secret \
    --tables=16 \
    --table-size=1000000 \
    --threads=$threads \
    --time=60 \
    run 2>&1 | grep -E "(transactions|queries|avg|95th|total time)"
  echo ""
  sleep 5
done

预期结果趋势

线程数    TPS        P95延迟(ms)
1         ~200       ~5
2         ~400       ~5
4         ~780       ~5
8         ~1400      ~6
16        ~2200      ~8        ← 性能拐点附近
32        ~2800      ~15       ← 延迟开始上升
64        ~3000      ~30       ← 接近系统极限
128       ~2900      ~60       ← 开始过载,TPS 下降
256       ~2500      ~120      ← 严重过载

4.7 不同 OLTP 测试的性能对比

测试类型预期相对 TPS典型瓶颈
oltp_point_select⭐⭐⭐⭐⭐ 最高索引查找、Buffer Pool 命中率
oltp_read_only⭐⭐⭐⭐ 高范围扫描、排序、临时表
oltp_write_only⭐⭐⭐ 中Redo Log、Undo Log、锁竞争
oltp_read_write⭐⭐⭐ 中读写混合,综合瓶颈
oltp_insert⭐⭐⭐⭐ 较高自增锁、B+Tree 分裂
oltp_update_index⭐⭐ 较低索引维护、锁竞争
oltp_delete⭐⭐ 较低B+Tree 合并、页分裂

4.8 OLTP 测试与数据库配置验证

4.8.1 Buffer Pool 大小验证

# 测试 1: Buffer Pool = 1GB
# my.cnf: innodb_buffer_pool_size = 1G
sysbench oltp_read_write \
  --mysql-host=127.0.0.1 --mysql-user=root \
  --tables=32 --table-size=1000000 \
  --threads=16 --time=300 run | tee bp_1g.txt

# 测试 2: Buffer Pool = 8GB
# my.cnf: innodb_buffer_pool_size = 8G
sysbench oltp_read_write \
  --mysql-host=127.0.0.1 --mysql-user=root \
  --tables=32 --table-size=1000000 \
  --threads=16 --time=300 run | tee bp_8g.txt

# 对比
echo "=== 1GB Buffer Pool ===" && grep -E "transactions|95th" bp_1g.txt
echo "=== 8GB Buffer Pool ===" && grep -E "transactions|95th" bp_8g.txt

4.8.2 Redo Log 大小验证

# 对比不同 innodb_log_file_size 的影响
# MySQL 8.0.30+ 使用 innodb_redo_log_capacity

# 测试小 Redo Log
mysql -e "ALTER INSTANCE DISABLE INNODB REDO_LOG;"
mysql -e "SET GLOBAL innodb_redo_log_capacity = '512M';"
sysbench oltp_write_only --mysql-host=127.0.0.1 --mysql-user=root \
  --tables=16 --table-size=1000000 --threads=32 --time=120 run | tee redo_512m.txt

# 测试大 Redo Log
mysql -e "SET GLOBAL innodb_redo_log_capacity = '4G';"
sysbench oltp_write_only --mysql-host=127.0.0.1 --mysql-user=root \
  --tables=16 --table-size=1000000 --threads=32 --time=120 run | tee redo_4g.txt

4.9 注意事项

4.9.1 数据一致性

  • OLTP 测试会修改数据,prepare 后的数据分布会随着 run 而变化
  • 多次测试前建议重新 prepare,确保数据一致性
  • oltp_read_write 中的 DELETE 会删除行,导致表数据逐渐减少

4.9.2 prepare 性能

大量数据的 prepare 可能耗时较长:

# 优化 prepare 速度的 MySQL 参数
mysql -e "SET GLOBAL innodb_flush_log_at_trx_commit = 0;"
mysql -e "SET GLOBAL sync_binlog = 0;"
mysql -e "SET GLOBAL innodb_autoinc_lock_mode = 2;"

# 准备完成后恢复
mysql -e "SET GLOBAL innodb_flush_log_at_trx_commit = 1;"
mysql -e "SET GLOBAL sync_binlog = 1;"

4.9.3 多表数量

  • 表数量应至少等于线程数,避免单表热点
  • 推荐:--tables >= --threads
  • 太多的表(>100)会增加 prepare 时间和元数据开销

4.10 小结

测试名称用途推荐场景
oltp_read_write读写混合全面性能评估(首选)
oltp_read_only只读读副本/报表查询评估
oltp_write_only只写写入性能/Redo Log 评估
oltp_point_select点查询索引性能极限测试
oltp_update_index索引更新索引维护开销评估
oltp_insert插入写入吞吐量测试

扩展阅读