MySQL性能压测实战:sysbench场景设计与关键指标分析完整指南
大家好,我是数据库小学妹👋 我踩过的坑,你别再踩。
六月份大促前一周,领导让我做一轮数据库压测,确认能不能扛住预计的三倍流量。我之前只跑过几次零散的基准测试,从来没有完整做过一次系统性的压测。接到任务后花了两天时间搭环境、调参数、写脚本、跑测试、分析结果,最后发现几个关键配置项调优后TPS从3000提升到了12000。
压测不是跑个工具看个数字就完了。场景设计错了,压出来的数据和真实负载完全对不上;指标看错了,你以为扛得住,上线后直接翻车。今天把压测的全流程完整梳理一遍,从环境准备到场景设计,从指标解读到配置调优,希望我踩过的坑能帮你少走弯路。
一、压测环境准备
压测环境必须和生产环境一致或可对比,CPU核心数、内存大小、磁盘类型是SSD还是HDD、网络带宽、MySQL版本。如果压测环境用HDD而生产用SSD,压出来的数据没有任何参考价值。
我的压测环境是8核16G、SSD云盘、MySQL 8.0.36。目标生产环境是16核64G、SSD、同版本MySQL。压测结果需要按比例估算,但不能简单线性外推。I/O和CPU在不同规格上的表现是非线性的。
安装sysbench很简单,大部分Linux发行版的包管理器都有:
# CentOS/RHEL
yum install sysbench
# Ubuntu/Debian
apt-get install sysbench
数据准备阶段需要先创建一个测试库和测试用户:
CREATE DATABASE sbtest;
CREATE USER 'sbtest'@'%' IDENTIFIED BY 'your_password';
GRANT ALL ON sbtest.* TO 'sbtest'@'%';
FLUSH PRIVILEGES;
然后用sysbench准备测试数据:
sysbench oltp_read_write \
--db-driver=mysql \
--mysql-host=127.0.0.1 \
--mysql-port=3306 \
--mysql-user=sbtest \
--mysql-password=your_password \
--mysql-db=sbtest \
--tables=10 \
--table-size=1000000 \
--db-ps-mode=disable \
prepare
这里创建了10张表,每张表100万行。table-size的大小会影响测试结果的准确性——太小测试不出大数据量下的性能衰减,太大准备数据耗时太长。我建议表行数至少是生产环境单表行数的十分之一以上,这样索引结构和数据分布才接近真实情况。
特别注意:sysbench 1.0.20对MySQL 8.0的prepared statement支持不稳定,不加--db-ps-mode=disable的话容易误报MySQL thread stack overrun错误,导致压测中断。
二、四种核心压测场景设计
很多新手压测只跑一个OLTP混合读写场景,然后就说“TPS能到8000,稳了”。这完全不够。数据库在不同负载模式下的表现差异巨大,只测一种场景等于只做了一种体检项目。
场景一:OLTP读写混合(模拟日常业务)
sysbench oltp_read_write \
--db-driver=mysql \
--mysql-host=127.0.0.1 \
--mysql-port=3306 \
--mysql-user=sbtest \
--mysql-password=your_password \
--mysql-db=sbtest \
--tables=10 \
--table-size=1000000 \
--threads=32 \
--time=300 \
--report-interval=10 \
--db-ps-mode=disable \
run
这个场景模拟OLTP系统的典型负载,约70%的读操作(点查加范围查询)和约30%的写操作(INSERT、UPDATE、DELETE的组合)。这是最基础的压测场景,反映的是日常业务的平均性能。
场景二:只读场景(模拟报表/查询负载)
把oltp_read_write换成oltp_read_only。这个场景对读缓存和索引效率非常敏感,用来评估系统在报表查询、只读分析场景下的能力。
场景三:只写场景(模拟OLTP写入负载)
换成oltp_write_only。这个场景模拟的是OLTP写入操作(包含INSERT、UPDATE、DELETE的组合),对写入缓冲、Redo Log刷盘策略、主键索引维护非常敏感。我在这个场景下发现了innodb_flush_log_at_trx_commit=1(每次事务刷盘)导致的写入瓶颈,改成2后写入TPS提升了近40%。
场景四:点查场景(模拟高并发主键查询)
换成oltp_point_select。这个场景模拟的是高并发下的单行主键查询,主要测试缓冲池命中率和锁竞争。如果点查性能差,说明Buffer Pool不够大或者存在锁竞争。
每个场景我跑300秒(5分钟),取稳定阶段(前30秒预热后)的数据。预热阶段的数据不准确,因为MySQL还在加载数据到Buffer Pool,命中率在爬坡阶段。
三、关键指标解读
跑完四个场景后,sysbench会输出一长串数据。但真正值得关注的,其实就四个核心指标。
每秒事务数(TPS):事务是一组SQL操作的集合,sysbench的OLTP事务包含SELECT、UPDATE、INSERT、DELETE。TPS反映的是系统的整体处理能力。我初始压测的读写混合场景TPS是3200左右,调优后达到11500。
每秒查询数(QPS):QPS通常远大于TPS,因为一个事务包含多条SQL。QPS更多反映的是查询层的吞吐能力。
延迟分位数(Latency Percentiles):这个指标比平均值重要得多。平均值20ms看起来很好,但如果P99延迟是500ms,意味着1%的请求要等半秒以上。大促时这1%的用户就会感觉系统卡顿。我重点关注P95和P99——P95代表95%的请求都在这个时间以内完成,P99代表99%的请求都在这个时间以内完成。初始配置的P99延迟在800ms左右,调优后降到120ms。
错误率:压测中出现的超时错误和连接拒绝。错误率应尽可能接近0,具体可接受的阈值需根据业务场景设定——金融场景可能0.01%都不能接受,而日志类业务可能容忍度更高。我在只写场景下出现过5%的超时错误,排查下来是innodb_thread_concurrency限制太紧导致的线程饥饿。
这四个指标需要组合判断:TPS高不代表系统健康。如果TPS很高但P99延迟也高,说明系统在处理大量请求但每个请求都很慢,这是典型的过载表现。正确的判断方式是在P99延迟不超过业务可接受阈值的前提下,TPS越高越好。
四、从压测结果反推配置优化
我的压测调优经历了三个轮次,每轮调一个或两个参数,跑完四组场景看变化。
第一轮:Buffer Pool调大
初始innodb_buffer_pool_size是默认值128MB,对于16G内存的服务器来说太小了。改成8G(总内存的50%)。这个改动对读场景的效果最明显——只读场景TPS从4500提升到7800,因为大部分查询都能从Buffer Pool直接拿到数据,不需要读磁盘。
SET GLOBAL innodb_buffer_pool_size = 8589934592;
MySQL 8.0支持在线调整Buffer Pool大小,不需要重启。
第二轮:Redo Log刷盘策略调整
初始innodb_flush_log_at_trx_commit=1,每次事务提交都强制刷盘,保证数据不丢,但写入性能差。改成2后,每次事务提交写操作系统缓存,每秒刷一次盘。写入场景TPS从2800提升到3900,提升了约40%。
这个改动的代价是:如果服务器断电,最多丢失1秒内的事务数据。对于大多数业务来说这是可以接受的风险。金融级业务建议保持=1,配合sync_binlog=1组成“双1配置”,这是行业标准做法。
第三轮:并发线程数调优
初始innodb_thread_concurrency没设限制(默认0是自动管理),在高并发写入场景下出现线程竞争。改成16后,写入TPS稳定在4200左右——之前波动很大,在2800到3800之间跳。这个参数没有标准答案,需要根据CPU核心数和业务负载模式逐步测试,找到最优值。
最终四轮测试的结果对比:
| 场景 | 初始TPS | 最终TPS | 初始P99延迟 | 最终P99延迟 |
|---|---|---|---|---|
| 读写混合 | 3200 | 11500 | 820ms | 120ms |
| 只读 | 4500 | 7800 | 350ms | 45ms |
| 只写 | 2800 | 4200 | 1200ms | 380ms |
| 点查 | 8500 | 15000 | 50ms | 15ms |
五、常见压测误区
很多人压测的结果不可用,不是因为工具不好,而是因为方法错了。
第一个误区是用空数据库压测。 刚初始化好的数据库Buffer Pool是空的,数据还没加载,索引也没建立好统计信息。这时候压出来的数据偏低,不能反映真实性能。正确的做法是先做一轮预热——跑一遍全表扫描或随机查询,让数据加载到Buffer Pool,然后再跑正式压测。
第二个误区是压测时间太短。跑30秒就拿结果?这30秒可能正好处于预热阶段或数据刷盘阶段,数据波动大。每个场景至少跑300秒,取稳定区间(排除前30秒预热)的平均值。
第三个误区是只看TPS不看延迟。TPS高但延迟也高的系统,上线后一定会出问题。大量请求在排队处理,用户体验很差。压测报告必须同时给出TPS和P99延迟,并且明确“在P99延迟不超过X ms的条件下,TPS达到Y”。
第四个误区是压测数据和生产数据结构不一致。sysbench默认的oltp测试表结构很简单,只有id、k、c、pad四个字段,没有复杂索引、没有TEXT字段、没有联合索引。如果你的生产表有大量复合索引、全文索引、JSON字段,用sysbench默认模板压出来的结果和生产环境差距很大。我的做法是根据生产表的DDL结构手动创建类似的测试表,再用sysbench的lua脚本做自定义压测。
六、避坑清单
压测环境的磁盘IO性能要和生产一致。我在云服务器上压测时用的是本地SSD,但生产用的是SSD云盘,两者的IOPS和吞吐量不是一个量级。压测结果出来后才发现,同样的配置下云盘的写入TPS只有本地SSD的60%。后来换了和生产一样规格的EBS云盘重新压测,数据才准确。压测前一定先确认存储类型和IOPS上限。
压测时关闭所有非必要的后台进程。MySQL的自动统计信息收集、备份任务、慢查询日志的flush操作——这些后台进程在压测期间会占用CPU和IO资源,导致测试结果偏低且不稳定。我的做法是压测前停掉crontab里的所有定时任务,压测结束后再恢复。但需要注意:在生产环境做压测时,关闭备份任务等操作要谨慎评估风险,避免影响生产数据的正常备份。
每次只调一个参数。这是压测调优的铁律。如果一次改了三个参数然后TPS提升了,你根本不知道是哪个参数的功劳,也不知道另外两个参数有没有副作用。我的做法是建一个调优记录表,每次记录改了哪个参数、改前值、改后值、每个场景的TPS和延迟变化。跑了五轮之后,就能清晰地看到每个参数对性能的影响趋势。
我是数据库小学妹,咱们下篇见 👋
*MySQL 8.0已于2026年4月正式结束生命周期(EOL)。如果你正在搭建新的压测环境或准备上线新系统,建议使用MySQL 8.4 LTS或9.7 LTS版本。8.4 LTS是官方推荐的长期支持版本,维护周期更长,安全更新更有保障。本文中的压测方法和参数调优思路同样适用于8.4和9.7版本。
- 点赞
- 收藏
- 关注作者
评论(0)