☰
sysbench压测实战:CPU内存基准与MySQL读写压测
2026/10/13 12:20:30 网站建设 项目流程

sysbench 这个压力测试工具,我前后用了快十年。从最早拿它压本机 CPU、内存带宽,到后来专门用来做 MySQL 的读写基准验证,它在我的工具箱里一直是"参数直白、行为可预期"的存在。现在的压测工具多了不少,有图形化的、有出报告特别漂亮的,但我做上架前验收、调优前后对比、容量评估这类事情时,还是会第一时间掏出 sysbench。

这篇文章不是一个命令手册的翻译,而是我多年实测下来的经验总结。我会讲清楚它到底能压什么、每条参数背后的含义、输出数据怎么读,以及最重要的——一个完整的 MySQL 读写压测应该怎么做。内容对刚接触服务器性能测试的新手友好,也能让已经用过一段时间、但只停留在"跑个默认 cpu 测试"阶段的人真正进阶。

1. 为什么压测我还是会掏出 sysbench 而不是更花哨的工具

先说结论:sysbench 的价值在于它的覆盖面广和行为可预期。它不是一个只盯着数据库的压测工具,而是一个通用系统基准测试工具。CPU、内存、文件 I/O、线程调度、互斥锁,再到关系型数据库的 OLTP 基准,全都能测。我经常在一个新服务器上用它五分钟内先跑完 CPU 和内存基准,确认硬件没有明显问题,然后再进入数据库层面的压测。一个 Jenkins 任务调用的就是同一套命令,跑进 CI 后它的稳定性和输出可解析性,比那些"仪表盘华丽"的工具强得多。

很多人会问:压数据库我用 mysqlslap 或者 pgbench 不就行了?确实行,但有一个区别。sysbench 的 OLTP 脚本本质是用 Lua 写的、自带事务编排逻辑的一套模拟协议,它模拟的是"多个客户端会话持续执行增删改查并提交事务"的业务负载,而不是像 mysqlslap 那样单纯并发跑几条 SQL。更关键的是,sysbench 支持Lua 脚本自定义。你可以按自己的业务表结构去造数据、去写查询语句,而不是被困在默认的几条 SQL 里。这一条就让它比很多专用工具灵活。

我归纳了一下平时最常用的场景:

场景使用模块我一般看的关键指标
新到货服务器的 CPU 性能验证cpuevents per second
内存带宽与延迟粗检memory吞吐量 MB/s,延迟分位
磁盘/云盘的裸性能测试fileioIOPS 与延迟分位
云服务器连续多轮跑分对比cpu+memory多轮结果稳定性
数据库读写与事务能力评估oltp_read_write等TPS、QPS、延迟分位
SQL 脚本/业务流改造验证自定义 Lua按具体业务定义成功率

从这张表也能看出来,sysbench 在"系统级基准"和"数据库级压力"两个层面都站得住。这是很多工具做不到的,因为它们要么只能压 CPU,要么只能压 SQL。

另外,我比较看重的一点是它的跨版本稳定性。我最早用的 0.4,到后来的 1.0,再到现在的 1.1,核心参数的命名和输出格式基本延续了下来。这意味着我五年前写的压测脚本,现在只需要改极少的地方就能继续用。做性能测试最怕的就是工具版本一换、参数全变,导致历史数据没法对比。sysbench 在这方面给了我很大的安全感。

2. 装好之后第一件事:跑通 sysbench 自带的 cpu 和 memory 基准

sysbench 的安装没有太多可说的,但我还是想提醒一个事:不要把发行版自带的旧版 sysbench 当成最终版本直接用。有些老系统的源里还是 0.4 甚至更早的版本,它的参数体系和新版差得很大。你搜网上教程的时候如果发现命令写的是--test=cpu这种老格式,那大概率是旧版。我建议直接用新版,自己编译也不麻烦。

2.1 安装中的两个实际注意点

在新一点的 Debian/Ubuntu 上用apt install sysbench,CentOS/Rocky 上用dnf install sysbench,通常都能拿到 1.0 以后的版本。但这些发行版自带包可能不是最新修复版,如果你对某些 OLTP 脚本的细节行为有要求,我更推荐源码安装:

curl -L -o sysbench.tar.gz https://github.com/akopytov/sysbench/archive/refs/tags/1.1.0.tar.gz tar xf sysbench.tar.gz cd sysbench-1.1.0 ./autogen.sh ./configure --with-mysql make -j$(nproc) sudo make install

这里有个细节:configure时我习惯加上--with-mysql,这样会同时编译出数据库压测所需的oltp_*.lua脚本和驱动。如果你只做 CPU、磁盘类的测试,不加 MySQL 支持也行,但数据库压测那章的内容就做不了了。编译前机器上需要有libmysqlclient-dev这类开发包,缺了它会直接提示找不到 mysql.h。

装完之后务必确认一下版本,我一般这么看:

sysbench --version

如果输出sysbench 1.1.0,脚本目录在/usr/local/share/sysbench/下能找到oltp_read_write.lua等文件,说明装得没问题。这个脚本目录路径很关键,后面跑数据库压测时你总会跟它打交道。

2.2 首次压测:看清 CPU 输出中的这几个数字

装好后,我建议新手跑的第一个测试不是数据库,而是 CPU 基准。因为它不需要任何前置准备,一条命令见真章:

sysbench cpu --threads=4 --time=30 --events=0 run

跑出来的结果里,最关键的是events per second,它代表每秒事件数。sysbench 的cpu模块做的事很简单:每个线程循环计算指定范围内的质数,每算完一轮记一个 event。所以这个数字越大,说明 CPU 在这个特定计算模型下跑得越快。

这块我给一个非常实用的经验:连续跑三到五次,看事件数是否稳定。如果数值上下波动超过 3% 到 5%,先别急着下结论,要确认机器的频率策略。现在的服务器 CPU 有睿频机制,压测前系统负载低时,前几秒频率可能没拉满,容易让人误判性能。我一般会在正式测试前先跑一个--time=5的预热,让频率和温度都稳定下来,然后再用--time=60做正式记录。

顺带一提,--events=0表示不限制总事件数,只按时间停止。--time=30则是最大运行 30 秒。很多教程会漏掉--events=0,导致压测在某个固定事件数下提前结束,数据不够充分。你要根据场景自己选,但我在做系统级测试时更常用"按时间结束"的模式。

2.3 内存测试的误区:它测的不是"容量",而是"搬运速度"

内存测试的常见误解是觉得它能测试内存容量是否正确、ECC 是否生效。实际上,sysbench 的memory模块做的是读写内存块操作的吞吐和延迟,本质是测试内存带宽和访问延迟,不是测试容量。

我常用的内存测试命令:

sysbench memory --threads=2 --memory-block-size=1M --memory-total-size=10G --memory-oper=read run sysbench memory --threads=2 --memory-block-size=1M --memory-total-size=10G --memory-oper=write run

这里需要解释两个参数。--memory-block-size是每次操作搬运的数据块大小,块越大越能体现连续内存的带宽;--memory-total-size是所有线程总共搬运的数据量,10G是我在内存大于 16G 的机器上常用的量级。数据量太小容易受缓存影响,数据量太大又会让测试时间变得过长。

读操作和写操作要分开测,因为大部分场景下读吞吐会比写快。输出里的Total operations、MiB transferred、MB/sec这三个值比较关键,其中MB/sec就是内存吞吐。做多线程测试时注意--threads不要无脑开到几十甚至上百,因为内存控制器本来就能并行处理一定数量的请求,线程数太多反而会让锁竞争干扰测试目标。我在单路服务器上的经验值是 CPU 核数的一半到两倍之间,别超过太多。

3. 数据库压测的关键:看懂输出、配好参数、改对自己的脚本

如果说 CPU 和内存测试只是热身,那 sysbench 真正让人又爱又恨的地方是它的数据库压测。爱是因为它足够强大,恨是因为参数太多,输出数据结构复杂,稍不留神就跑出不具参考意义的数据。

跑数据库压测之前,先理解它的整体三段式流程:

prepare阶段:建表、插入指定数量的测试行 run阶段:真正并发执行压测脚本 cleanup阶段:清理掉测试表

这三阶段用的基本是同一个脚本入口,靠prepare、run、cleanup三个子命令区分。我第一次用的时候没搞清这个逻辑,直接跑run结果报错说表不存在,后来才知道要先prepare。现在倒觉得这个三段式设计很干净,它把"造数据""跑压力""收尾"彻底分开了,特别适合自动化集成。

3.1 核心参数对照表:一眼看清每个参数的用途

我把平时最常用的参数整理成一张表,方便你查:

参数含义我的常用值备注
--threads=N并发线程数16/32/64不是越大越好,见下文
--events=N总事件数上限00 表示不限制,按时间跑
--time=N最大运行秒数60/120现场压测我常用 300
--report-interval=N每 N 秒打印一次中间统计10用于观察波动
--rate=N限制每秒事件数不设置主要用于限速模拟
--tables=N测试表数量16多表更贴近真实业务
--table-size=N每张表的行数100000行数过少缓存干扰大
--mysql-host/--mysql-port数据库地址端口-本机测试也要写,走 TCP
--mysql-user/--mysql-password账号密码-建议专用账号
--mysql-db目标库sbtest需要提前创建
--db-driver=mysql驱动类型mysql压 pg 则写 pgsql
--db-ps-mode=disableSQL 预处理开关disable见下文解释

这里我得专门展开说一个参数:--db-ps-mode=disable。默认情况下 sysbench 会使用 MySQL 预处理语句(prepared statement),这能让 SQL 执行计划在多次执行中被复用,缩短单条 SQL 的耗时。但如果你压测的目标是排查慢查询、评估真实业务的 SQL 解析与执行全链路成本,预处理会掩盖一部分开销。我习惯在 OLTP 基准中用disable,把 SQL 准备过程也纳入压力范围,这样跑出来的结果更贴近一个"不懂复用执行计划的普通业务连接"的真实表现。当然,如果你想模拟大量使用 ORM 预处理语句的系统,那就保持默认的自动模式。这个选择会直接影响你的 TPS 数据,对比测试时前后要一致。

3.2 输出数据读法:TPS、QPS、百分位延迟各有各的读法

第一次跑 sysbench 数据库压测的时候,很多人会懵:屏幕上既有一堆read/write的吞吐数据,又有一个延迟汇总,还时不时刷新中间报告。完整结果里最核心的是两部分:

SQL statistics: queries performed: read: 425600 write: 121600 other: 51200 total: 598400 transactions: 6400 (1280.00 per sec.) queries: 85120 (17024.00 per sec.) ignored errors: 0 (0.00 per sec.) reconnects: 0 (0.00 per sec.) Latency (ms): min: 23.45 avg: 127.38 max: 984.22 95th percentile: 412.76 sum: 815232.11

第一行的transactions后面的括号数字就是TPS,即每秒事务数。第二行的queries括号数字是QPS。这两个数字是整个压测报告最核心的指标。事务在这里指一组完整的读写 SQL 组合,一次事务可能包含多条 SQL,所以 QPS 一般远高于 TPS。

延迟统计里,avg是所有事务的平均耗时,95th percentile表示有 95% 的事务延迟低于这个数。我特别建议你先看95th percentile而不是avg,因为数据库偶尔会出现长尾请求,把平均值拉高,但 95 分位能更真实反映"绝大多数请求体感多快"。比如上面的结果里 avg 是 127ms,看起来还行,但 95 分位已经到 412ms,说明尾部请求有明显卡顿。这种数据如果只看 avg,你很可能会误判系统性能。

中间过程报告来自--report-interval=10这类参数。它能在跑的过程中每 10 秒刷新一次 tps、qps、延迟数据。做现场故障排查时我特别依赖这个输出:它能让我看到压力慢慢上来时性能是不是线性变化,还是到了某个时间点突然断崖式下跌——后者往往意味着发生了锁等待、连接耗尽或内存交换。

3.3 为什么我不建议无脑把 --threads 拉到上百

有个非常经典的坑:把--threads从 16 翻到 64,TPS 可能涨了,但延迟跟着也涨了;再翻到 128,TPS 反而可能下降。原因很简单,并发线程数超过数据库处理能力后,请求开始排队,而不是并行处理。排队时间越长,延迟越高,TPS 不再增长甚至因为资源竞争而下滑。

我做容量评估时一般这样测试:从 8 个线程开始,按照 16、32、64、128 的梯度依次跑,每轮跑完记录 TPS 和 95 分位延迟,然后画一条"并发数—TPS"的曲线。曲线上最后那个"TPS 增长幅度小于 5% 的线程数",基本就是这个系统的建议并发上限。这个值对后续配置连接池大小非常有参考价值——我见过不少团队的连接池配了 200 个连接,实际压测显示 64 连接时性价比最高,200 连接只是让线程全部堆积在数据库侧。

4. 一次完整的 MySQL 读写混合压测应该怎么做

前面讲了不少原理,现在来一次完整的实战。这一节我按自己常用的标准流程来走,你可以直接当模板用。

4.1 准备压测库、账号与表结构

先在 MySQL 里创建数据库和账号,这一步很多人会漏掉。sysbench 的prepare不会帮你创建数据库,只会建表。我用的一套标准准备 SQL 是:

CREATE DATABASE IF NOT EXISTS sbtest CHARACTER SET utf8mb4; CREATE USER IF NOT EXISTS 'sbtest'@'%' IDENTIFIED BY 'sbtest123'; GRANT ALL PRIVILEGES ON sbtest.* TO 'sbtest'@'%'; FLUSH PRIVILEGES;

为什么要单独建一个专用账号?因为压测时会产生大量连接,如果复用业务的正式账号,一来可能触发权限审计告警,二来错误日志里会被压测报错刷屏,影响你对真实问题的判断。专用的 sbtest 账号,用完随时可以清理,权限也限制在这个库内,风险可控。

然后跑prepare生成 16 张表,每张 10 万行:

sysbench /usr/local/share/sysbench/oltp_read_write.lua \ --mysql-host=127.0.0.1 \ --mysql-port=3306 \ --mysql-user=sbtest \ --mysql-password=sbtest123 \ --mysql-db=sbtest \ --tables=16 \ --table-size=100000 \ --threads=16 \ prepare

这条路走到一半最容易出的问题有几种:提示Table 'sbtest.sbtest1' doesn't exist,说明你还没跑过 prepare;提示权限不足,说明账号授权时没给到对应库;卡在插入很久没反应,多半是表数量和数据行数设得太大,或者磁盘刷脏页很慢。

我自己的经验是:压测前先跑通一次小数据量的全过程。比如先用--tables=4 --table-size=10000跑一遍 prepare→run→cleanup,确认脚本和参数没问题,再上正式容量。这能省下大量排错时间,因为你不会知道是脚本问题还是资源瓶颈。

4.2 正式压测:长时间比短时间更能说明问题

我见过很多人在压测时用--time=10,这是非常不推荐的做法。10 秒太短,数据库的缓冲池都还没热起来,CPU 频率也未必稳定,跑出来的数据只能算误差。我自己在现场做基准测试时,最少用 60 秒,正式记录用 120 秒,排查性能波动时会跑到 300 秒。

正式读写混合压测命令:

sysbench /usr/local/share/sysbench/oltp_read_write.lua \ --mysql-host=127.0.0.1 \ --mysql-port=3306 \ --mysql-user=sbtest \ --mysql-password=sbtest123 \ --mysql-db=sbtest \ --tables=16 \ --table-size=100000 \ --threads=32 \ --time=120 \ --report-interval=10 \ --db-ps-mode=disable \ run

跑的时候,我习惯在旁边开一个终端用top或者htop观察 CPU 和内存,再开另一个窗口看 MySQL 的show processlist。为什么要看这些?因为 sysbench 跑出来的数字再漂亮,也要结合服务器资源使用情况才能下结论。比如 TPS 只有 500,CPU 利用率却已经 100%,说明瓶颈可能在 CPU 计算而不是数据库层面;如果 TPS 使用 32 线程和 16 线程差不多,CPU 也没吃满,那就要怀疑是不是锁等待或者 IO 卡顿。

我还会顺带看 MySQL 的线程状态。如果 processlist 里大量线程卡在statistics或Sending data,那通常是单条 SQL 性能差导致的;如果大量线程卡在Updating或update,则更可能是写锁竞争。这些东西 sysbench 本身并不会直接告诉你,需要你人肉关联来看,这也正是"实战"二字比"跑命令"更值钱的地方。

4.3 压完怎么清理数据

压测完成后不要忘了清理环境。虽说留着测试表问题也不大,但如果你是在生产库旁边开的临时环境,或者压测完后要继续装监控系统,测试表会污染后续指标。清理命令很简单:

sysbench /usr/local/share/sysbench/oltp_read_write.lua \ --mysql-host=127.0.0.1 \ --mysql-port=3306 \ --mysql-user=sbtest \ --mysql-password=sbtest123 \ --mysql-db=sbtest \ --tables=16 \ --table-size=100000 \ cleanup

清理之后确认一下SHOW TABLES;是否为空。有些版本在清理过程中如果遇到连接中断,可能会残留部分表,需要手动DROP TABLE。这个细节很小,但对自动化脚本的幂等性影响很大——一个重新执行清理会报错的脚本,集成到 CI 里会非常难受。

5. 自定义 Lua 压测脚本:让 sysbench 真正贴合业务

默认的oltp_read_write是一个通用的 TPCC 风格基准,简单好用,但它和你的真实业务相比,SQL 模型差异可能非常大。比如你的业务绝大部分是单条主键查询,很少写;再比如你有一个报表系统,查询时间很长、并发也不高。用默认脚本压出来的数据,只能代表"sysbench 模拟的通用负载",不能代表"你的业务跑在这个机器上的表现"。

所以真正会用 sysbench 的人,都会改脚本。

5.1 脚本结构骨架:三件事

一个最小可用的 Lua 脚本长这样。我习惯把它放在/root/scripts/或者项目目录下,文件名自取,比如my_select.lua:

function thread_init(thread_id) -- 每个线程开始时执行一次,在这里初始化专用连接或变量 end function thread_done(thread_id) -- 线程结束时执行,一般用来关闭连接 end function event() -- 这是核心函数,每触发一个 event 就执行一次 -- 一般在这里构造 SQL 并执行 local table_name = "sbtest1" local k = math.random(1, 100000) local rs = db_conn:query("SELECT c FROM " .. table_name .. " WHERE id=" .. k) end

sysbench 执行 Lua 脚本的机制是:每个 worker 线程在初始化时调用thread_init,然后不断循环调用event直到到达期望的--events总数或--time时间上限。event每执行一次,就算一个 event。所以events per second实际上就是"每秒执行了多少次event"。

理解这个结构之后,你就会明白自定义脚本的本质是:你只要控制event里执行什么 SQL,以及用多少个线程去跑它,就能模拟出特定形态的负载。

5.2 一个实际案例:单表点查压测

假设你要模拟"基于主键查询订单状态"的业务,并发基本都在做SELECT * FROM order_table WHERE order_id = ?。你可以写这样一个脚本:

function thread_init(thread_id) -- 注意这里不是 C API,脚本里的 db_conn 由 sysbench 运行时注入 end function event() local order_id = math.random(1, 200000) local query = string.format( "SELECT status, amount FROM order_table WHERE order_id = %d", order_id ) db_conn:query(query) end

对应的执行命令:

sysbench /root/scripts/my_select.lua \ --mysql-host=127.0.0.1 \ --mysql-port=3306 \ --mysql-user=sbtest \ --mysql-password=sbtest123 \ --mysql-db=your_db \ --tables=1 \ --table-size=1 \ --threads=32 \ --time=120 \ --db-ps-mode=disable \ run

注意我这里的--tables=1 --table-size=1是故意设的,因为自定义脚本的生产数据一般由你自己提前生成,不用 sysbench 的 prepare 数据结构。你需要保证order_table在测试库里真实存在并且有 20 万行左右的数据,否则随机查询的值可能大量落在表不存在的区域上,导致大量空结果和实际业务不符。

自定义脚本跑出来的结果,和默认脚本最大的不同是你清楚每个 event 代表什么。默认脚本的 event 是一个读写事务,包含好几个 SQL;你的点查脚本的 event 就是一条点查 SQL。这个"语义对应关系"对于向团队解释压测结果极其重要。

5.3 为什么我在脚本里尽量用随机数据而不是固定值

写自定义脚本时有个常见错误:为了省事,把查询条件写死成一个常量,比如WHERE status='SUCCESS'。这样跑出来的结果会严重失真,因为数据库缓存会把这一页数据一直留在缓冲池里,每次查询都命中缓存,I/O 完全压不出来。

真实的业务条件值总是有分布的。所以我在脚本里会先定义一个分布范围,再随机取样。就像上面的math.random(1, 200000)。这里推荐一个进阶玩法:如果你的业务符合热点分布(少数数据被高频访问),可以用sysbench.rand来生成不同分布的数据。sysbench 内置了好几种随机分布函数,重尾分布和正态分布的差异,对数据库缓存命中率的影响非常显著。

一段带分布控制的示例:

function event() local order_id local r = sysbench.rand.uniform(1, 100) if r <= 80 then order_id = sysbench.rand.uniform(1, 5000) -- 80% 的请求落在小范围热点 else order_id = sysbench.rand.uniform(5001, 200000) -- 20% 的请求落在长尾 end db_conn:query(string.format( "SELECT status, amount FROM order_table WHERE order_id = %d", order_id )) end

这样模拟出来的负载,缓冲池命中率会更接近真实业务——大部分请求命中同一批热门订单页,少量请求穿透到磁盘。我在某一次基于真实订单场景的压测里,用这种分布跑出来的 QPS 和线上监控里的 QPS 曲线高度吻合,而用默认均匀随机分布跑则严重偏低。这个经验后来一直被我当成"自定义脚本必须做的一点"来用。

6. 我踩过的坑、误差来源,以及怎么让结果可信

压测最忌讳的是"测了一堆数据,但没法用"。数字看起来很漂亮,换个环境时间再跑一遍,数据完全对不上。以下是我在这些年实际使用 sysbench 过程中踩过的坑和总结出的规避方法。

6.1 断崖式 TPS 变化先别怪数据库,检查这些

有一次我在压一个数据库集群,跑到第 40 秒左右 TPS 突然从 3000 掉到 800,并且持续低频振动。我第一反应是数据库出了问题,于是各种查慢查询、锁等待,都没发现异常。后来开了一个新窗口看 sysbench 所在机器的负载,才发现压测机本身 CPU 已经 100%。

办坏事的是测试机而不是数据库。sysbench 是一个单机工具,它所有线程都跑在发起压测的这台机器上。线程数开太高时,测试机自己的 CPU 会先饱和,导致它"造压力"的能力跟不上,TPS 自然掉下去。从那以后,我压测前会先确认测试机至少有 4 个空闲核心,或者把线程数控制在测试机核数的两倍以内。如果你发现异常,第一件事先看压测机的 CPU 和内存,再看数据库端,这个排错顺序帮我省了大量时间。

6.2 内存和缓存对压测结果的干扰

数据库压测时,MySQL 的innodb_buffer_pool_size会很大程度影响你的 TPS。同样的压测命令,在缓冲池只有 1G 和 8G 的实例上跑出来的结果差异可能在一倍以上。这不是 sysbench 的问题,而是数据库本来就该有缓存能力。所以我做"系统性能基准"时,一般会把缓冲池调到合理大小,比如环境总内存的 50% 到 60%,再做压测。

相对地,如果我想压的是"磁盘 I/O 能力"而不是"缓存命中能力",我会故意调小innodb_buffer_pool_size或者让测试数据量远大于缓冲池。比如--table-size=2000000,这样数据总大小远超过内存,查询就会真正打到磁盘,跑出的 TPS 才代表冷数据场景下的能力。

6.3 时钟与误报:sysbench 不背的锅

压力测试过程中,如果服务器开启了 NTP 时间同步,且时间跳跃幅度比较大,会导致 sysbench 的计时和输出数据出现"假波动"。一次压测中我明明看到 TPS 报表非常稳定,结果某个 10 秒间隔的中间报告突然中断了几秒,整体时间线也乱了。查下来发现是 NTP 在校时。后来我做长时间压测时,会临时停止 NTP 同步,或者接受这种轻微抖动,在结果分析时剔除明显异常的中间报告。

另一个被忽略的问题是ignored errors。sysbench 跑完会输出ignored errors,默认情况下它会把一些可忽略的数据库错误吞掉,不中断压力测试。如果你的脚本里有主键冲突、死锁重试这类情况,ignored errors会大于 0。我要提醒的是:压测报告里 TPS 归 TPS、ignored errors 归 ignored errors,不要视而不见。如果 errors 占事务比例超过 1%,我会认为这轮压测结果不可信,因为错误会产生锁等待、回滚,降低整体 TPS,这个数字不能代表系统正常能力。

我自己的判断标准是:压测开始前先跑一个 30 秒的短测试,确认ignored errors: 0。如果从第一轮开始就有大量错误,说明账号权限、表结构或者参数没配对,不适合急着拉长测试时间。

6.4 怎么让压测结果经得起推敲:多轮 + 标准差

最后说一下“可信度工程”。我不管做哪个环境、哪个模块的压测,都会重复跑至少三到五轮,然后取平均值和中位数来对比。关键是第二轮开始之前,要有冷却时间,让数据库缓冲池和内存状态回到一个相对正常的位置。我一般是每轮之间间隔 30 秒到 1 分钟,长压测间隔更长。

记录结果时我用的格式是:

第1轮 TPS/95ms: 3278 / 42ms 第2轮 TPS/95ms: 3301 / 41ms 第3轮 TPS/95ms: 3256 / 44ms 平均值: 3278,波动范围: -0.7% ~ +0.7%

如果某轮结果突然和其他几轮差异超过 10%,我不会直接删掉它,而是先排查那轮跑的时候有没有其他任务在抢资源。测试环境里跑着定时备份、日志压缩、监控抓包这些任务,都会让某一轮数据异常。把原因找出来再重跑,比单纯拿多个数字取均值要严谨得多。

还有一个小技巧:我把 sysbench 的中间报告(--report-interval产生的那些)都存到日志里,而不仅仅是最后汇总。因为最后汇总只能看到全局平均值,中间报告却能看出性能变化曲线。如果前 30 秒 TPS 稳定在 3000,后 30 秒降到 2000,那和 60 秒平均值 2500 是完全不同的两种故事。后者可能意味着缓冲池被撑满、磁盘 I/O 开始介入,或者数据库内存分配触发了 swap。这些细节如果不看中间报告,是永远发现不了的。


sysbench 不是那种装好后跑完一条命令就能收工的工具,它的真正价值在于你理解它每一层参数的含义,以及能把压测结果和系统资源关联起来解释。我自己现在做任何服务器的基准验收时,第一轮一定用固定的cpu --threads=4 --time=30快速确认硬件基础,第二轮用默认oltp_read_write做一个能横向对比的参考值,后面按业务需求写自定义 Lua 脚本做深入压测。这个流程跑了很多台机器,数据一直稳定,也方便跟往年记录做对比。如果你刚开始接触 sysbench,建议先把我前面说的 CPU 和内存测试跑熟,再进阶到数据库压测,别一上来就追求高并发——先把一两个指标的"为什么"弄明白,比刷高数字有用得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询