附-01 · MySQL 压测与容量规划 —— sysbench 基准测试、容量评估与参数基线

附-01 · MySQL 压测与容量规划 ------ sysbench 基准测试、容量评估与参数基线

系列定位 :《MySQL 数据库全栈运维实战系列》第 附-01 篇(番外),与《Oracle 数据库全栈运维实战系列(VMware 版)》同构对标。适用版本 :MySQL 8.0(5.7 差异随文标注)。官方文档:MySQL 8.0 Reference Manual、sysbench 官方文档(github.com/akopytov/sysbench)。


一、为什么压测先行

很多团队上线前不压测,上线后才发现"这台 32G 虚机撑不住 5000 TPS"。容量规划的核心逻辑是:先用 sysbench 在与生产同规格的虚机上跑出这台机器的 TPS 上限,再按业务峰值 × 安全系数(2~3 倍)倒推需要多少台、多大内存、什么磁盘。没有压测数据的容量规划就是拍脑袋。

图 1

概念 说明 类比 Oracle
sysbench MySQL 官方生态最常用的 OLTP 压测工具 Oracle Swingbench / ORION
TPS 每秒事务数 transactions per second
QPS 每秒查询数 ?
P99 延迟 99 分位响应时间 response time percentile
安全系数 峰值 × 2~3 倍留余量 capacity headroom

VMware 虚机环境注意事项:压测结果只在"与生产同规格、同置盘方式、同资源预留"的虚机上才有意义。压测前务必:① 固定 vCPU 数与内存(不要开热添加);② 磁盘确认 thick eager zeroed 或 SSD 直通(thin 盘在首次写入时会做 zeroing,污染结果);③ 关闭宿主机上其他大负载 VM;④ 压测分冷启动(重启 MySQL 后跑)和热数据(预热 Buffer Pool 后跑)两轮,分别记录。


二、sysbench 安装与三个阶段

图 2

复制代码
# CentOS 7 / Rocky Linux 安装(解释:官方 yum 源或直接下二进制)
curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.rpm.sh | sudo bash
sudo yum -y install sysbench

# 验证版本(解释:8.0 配套建议 sysbench 1.0.20+)
sysbench --version
# sysbench 1.0.20

sysbench 压测分三段:prepare(造数据)→ run(压测)→ cleanup(清数据)。

复制代码
# 通用连接参数(解释:抽到变量里,三段复用)
SB_HOST=127.0.0.1
SB_PORT=3306
SB_USER=sbtest
SB_PWD='sbtest_pwd'
SB_DB=sbtest

# 先建库和账号(解释:sysbench 不会自动建)
mysql -uroot -p -e "CREATE DATABASE sbtest;
                    CREATE USER 'sbtest'@'%' IDENTIFIED BY 'sbtest_pwd';
                    GRANT ALL PRIVILEGES ON sbtest.* TO 'sbtest'@'%';
                    FLUSH PRIVILEGES;"

2.1 prepare:造数据

复制代码
# 造 10 张表,每张 100 万行(解释:oltp_read_write 是经典 OLTP 模型)
sysbench --mysql-host=$SB_HOST --mysql-port=$SB_PORT \
         --mysql-user=$SB_USER --mysql-password=$SB_PWD \
         --mysql-db=$SB_DB \
         oltp_read_write \
         --tables=10 --table-size=1000000 \
         prepare

2.2 run:压测

复制代码
# 20 并发线程,跑 60 秒(解释:先短后长,60s 看趋势,再跑 300s 拿稳定值)
sysbench --mysql-host=$SB_HOST --mysql-port=$SB_PORT \
         --mysql-user=$SB_USER --mysql-password=$SB_PWD \
         --mysql-db=$SB_DB \
         oltp_read_write \
         --tables=10 --table-size=1000000 \
         --threads=20 --time=60 --report-interval=10 \
         run

典型输出(解释:重点看这几个数):

复制代码
SQL statistics:
    queries performed:
        read:                            456000
        write:                           128000
        other:                           64000
        total:                           648000
    transactions:                        32400  (540.0 per sec.)
    queries:                             648000 (10800.0 per sec.)
    ignored errors:                      0      (0.0 per sec.)
    reconnects:                          0      (0.0 per sec.)

Throughput:
    events/s (eps):                      540.00
    latency (ms):
          min:                            2.1
          avg:                           37.0
          max:                         480.5
          99th percentile:              85.2
    threads fairness:                      0.99

2.3 cleanup:清数据

复制代码
sysbench --mysql-host=$SB_HOST --mysql-port=$SB_PORT \
         --mysql-user=$SB_USER --mysql-password=$SB_PWD \
         --mysql-db=$SB_DB \
         oltp_read_write --tables=10 \
         cleanup

三、常用压测模型与场景选择

测试脚本 场景 适用业务
oltp_read_write 混合读写(默认 14 SELECT + 4 UPDATE + 2 INSERT + 2 DELETE) 通用 OLTP,最常用
oltp_read_only 只读 报表/读库
oltp_write_only 只写 写入密集型
oltp_point_select 单点主键查询 主键 KV 查询
oltp_update_index / non_index 索引列/非索引列更新 写放大评估
select_random_ranges 范围扫描 报表扫描
复制代码
# 压测读库(解释:从库/只读节点用 read_only 模型)
sysbench ... oltp_read_only --threads=50 --time=300 run

# 压测写能力(解释:主库写上限)
sysbench ... oltp_write_only --threads=32 --time=300 run

四、并发梯度:找到拐点

图 3

图 4

复制代码
# 梯度压测(解释:依次跑 10/20/50/100/200 并发,记录 TPS 与 P99)
for t in 10 20 50 100 200; do
  echo "=== threads=$t ==="
  sysbench ... oltp_read_write --threads=$t --time=60 run 2>&1 | grep -E "transactions:|99th"
done

典型拐点曲线:

并发线程 TPS P99 延迟 结论
10 380 25ms 未饱和
20 540 37ms 接近最优
50 620 95ms 到顶
100 580 220ms 开始下降(CPU/IO 饱和)
200 420 680ms 过饱和

结论:这台虚机在 50 并发附近 TPS 见顶(约 620),P99 < 100ms。业务峰值按 200 TPS 算,单机够;按 1000 TPS 算,要 2 主从读写分离。


五、容量评估方法论

图 5

图 6

5.1 内存怎么算

项 经验值
innodb_buffer_pool_size 物理内存 60~70%( dedicated DB 虚机)
每个连接线程内存 sort_buffer + join_buffer + read_buffer ≈ 4~8MB/连接
其他(OS、连接、tmp table) 留 2~4G

32G 虚机:BP=20G,max_connections=500,预留 OS+连接约 8G,刚好。

5.2 磁盘怎么算

复制代码
总磁盘 = 数据 + 索引 + redo + binlog(7天) + 备份空间 + 30% 余量
  • 数据:sysbench 跑出单行大小 × 预估行数。
  • binlog:按峰值 TPS × 平均 row 大小 × 86400 × 7 天估。
  • 备份:至少保留 2 份全备 + 当天增量,建议数据盘的 1.5 倍。

5.3 IOPS 怎么算

复制代码
# 压测时同时盯主机 IO(解释:iostat 看 %util 和 await)
iostat -xz 2 5
# 如果 await > 20ms 且 %util 接近 100%,说明磁盘是瓶颈,要换 SSD/加盘

六、参数基线(8.0 生产推荐 my.cnf)

复制代码
[mysqld]
# ===== 基础 =====
user            = mysql
port            = 3306
datadir         = /data/mysql
socket          = /data/mysql/mysql.sock
character-set-server = utf8mb4
collation-server    = utf8mb4_0900_ai_ci

# ===== 内存(按物理内存 60~70%)=====
innodb_buffer_pool_size = 20G          # 32G 虚机示例
innodb_buffer_pool_instances = 8
innodb_log_buffer_size = 64M

# ===== redo(8.0.30+ 统一参数)=====
innodb_redo_log_capacity = 2G
innodb_flush_log_at_trx_commit = 1      # 双 1,不动
innodb_flush_method = O_DIRECT

# ===== binlog =====
log_bin = /data/mysql-bin/mysql-bin
binlog_format = ROW
binlog_expire_logs_seconds = 604800    # 7 天
sync_binlog = 1                        # 双 1,不动

# ===== 连接 =====
max_connections = 500
wait_timeout = 60
interactive_timeout = 300

# ===== 临时表/排序 =====
tmp_table_size = 128M
max_heap_table_size = 128M
sort_buffer_size = 2M
join_buffer_size = 2M

# ===== IO(SSD 直通盘)=====
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000

# ===== 慢日志 =====
slow_query_log = 1
long_query_time = 1
slow_query_log_file = /data/mysql-slow/slow.log

类比 Oracle:Oracle 容量规划用 ORION 压磁盘 IO、Swingbench 压业务负载;MySQL 生态对应就是 sysbench(业务负载)+ fio(裸盘 IO)。Oracle 有 Automatic Memory Management,MySQL 内存参数要自己按上面公式算,没有自动 SGA 调优。


七、上线后基线对比

压测不是一次性动作。上线后每周把生产监控曲线和压测基线对比:

指标 压测基线 生产实测 偏差
50 并发 TPS 620 580 -6%(正常抖动)
Buffer Pool 命中率 99.9% 99.2% 略低,关注
P99 延迟 85ms 120ms 偏高,查慢 SQL
磁盘 await 5ms 18ms IO 紧张,考虑 SSD

八、踩坑记录(实测汇总)

# 坑 现象 解决
1 在 thin 盘上压测 首次写入 zeroing,TPS 忽高忽低 thick eager zeroed 或 SSD
2 压测没预热 第一轮冷数据全落盘,TPS 极低 先跑一轮 prepare 后预热再正式压
3 压测机和 MySQL 同机 CPU/IO 互相干扰 压测客户端单独一台虚机
4 并发一路加到 1000 过饱和后 TPS 反而掉 找拐点,别盲目加并发
5 拿默认参数压测 Buffer Pool 才 128M,结果不可信 先按基线 my.cnf 调优再压
6 只看 avg 延迟 avg 37ms 但 P99 480ms 必看 99th percentile
7 业务量拍脑袋 上线 3 个月磁盘满 按数据增长模型提前 6 个月扩
8 备份空间不算进总盘 全备没地方放 总盘预留备份 1.5 倍数据量
9 VMware 没关其他 VM noisy neighbor 污染数据 压测窗口独占宿主机
10 压测脚本用 oltp_read_only 测写库 结果完全失真 按业务类型选脚本

九、本文要点速记

  1. 压测先行:同规格虚机上跑 sysbench,拿到单机 TPS 上限再做容量规划。
  2. 三段式:prepare → run → cleanup,别忘 cleanup。
  3. 找拐点:梯度加并发,TPS 见顶 + P99 飙升处就是最优并发。
  4. 内存公式:Buffer Pool 60~70% 物理内存,连接内存按 max_connections×per-thread 算。
  5. 双 1 不动:压测时别为了好看把 flush_log/sync_binlog 关了,那不是生产配置。
  6. 上线后对比基线:每周看生产指标 vs 压测基线,偏差超过 20% 要查原因。

十、参考资料(官方正式文档)

主题 官方链接
MySQL 8.0 Reference Manual(InnoDB 配置) https://dev.mysql.com/doc/refman/8.0/en/innodb-configuration.html
sysbench(GitHub 官方仓库) https://github.com/akopytov/sysbench
sysbench OLTP 脚本说明 https://github.com/akopytov/sysbench/tree/master/sysbench/tests/include/oltp_legacy
fio(裸盘 IO 压测) https://github.com/axboe/fio
InnoDB Buffer Pool 配置 https://dev.mysql.com/doc/refman/8.0/en/innodb-buffer-pool-configuring.html
Binary Log 配置 https://dev.mysql.com/doc/refman/8.0/en/binary-log-config.html

本文章属于《MySQL 数据库全栈运维实战系列》番外篇。欢迎收藏、交流。

相关推荐
这个DBA有点耶1 小时前
主从切换报成功,业务却写不进去——数据层脑裂与应用层连接残留的两类假成功
数据库·架构·dba
qq_452396231 小时前
第八篇:《VACUUM、自动清理与膨胀治理》
jvm·数据库·oracle
SelectDB技术团队1 小时前
ClickHouse 支持存算分离吗?Doris 社区版原生存算分离怎么落地
数据库·clickhouse·云原生·开源·apache doris·技术选型·存算分层
mennekes1 小时前
暗装插座箱 VS 明装插座箱,工地和车间怎么取舍
运维·网络·科技·安全·制造
叼烟扛炮1 小时前
Linux 学习笔记(三十六):并发进阶——读写锁 vs 自旋锁,到底怎么选?
linux·运维·服务器·笔记·学习
DBA_G1 小时前
南大通用GBase 8a数据库可视化灾备解决工具GVR
数据库
正经教主1 小时前
【FDE系列】阶段2:Day 43(UP详细版):Docker Compose — 多容器一键编排
运维·docker·容器
l1t1 小时前
DeepSeek总结的PostgreSQL 中的 SQL 与 PL/pgSQL:计划缓存
sql·缓存·postgresql
大耳朵爱学习1 小时前
Andrew Ng深度解析:普通人如何利用AI把想法变成工具,实现工作自动化(小白程序员必看)
运维·人工智能·ai·自动化·大模型·就业·大模型学习