1. tpcc-mysql 与 TPC-C 的对应关系:为什么端口会成为第一道坎
把一套数据库基准测试跑起来,最磨人的地方往往不是业务模型有多复杂,而是最开始那一下连不上。我见过太多人卡在这一步:库部署在 3307 端口,压测工具的参数照着教程敲下去,回车之后屏幕上只有一句冷冰冰的Can't connect to MySQL server on '127.0.0.1' (111),然后开始怀疑网络、怀疑防火墙、怀疑服务没起来,折腾两个小时才发现是参数写错了。tpcc-mysql 这套工具本身不复杂,但它有几个设计上的历史包袱,尤其是连接参数的处理方式,非常容易让人在端口这件事上栽跟头。
先把话说明白,tpcc-mysql 是一套用来做 TPC-C 基准测试的开源工具集,主体是一堆 C 源码,编译出两个可执行程序:一个负责建库灌数据,一个负责发起压测。很多资料里把后者叫 TPCCRunner、tpcc-runner 或者 tpcc_start,叫法不统一,但指的是同一个东西——那个模拟终端用户不停下单、付款、查订单、发货、查库存的运行器。它能告诉你一套 MySQL 实例在标准交易模型下每分钟能跑多少个新订单,也就是大家常说的 tpmC。适合谁来参考?准备做数据库选型对比的、要给新硬件出一份基准报告的、怀疑线上配置没调好想验证一下的,以及单纯想搞明白 TPC-C 到底测的是什么的人。
1.1 五类业务事务落到具体表上是什么样子
TPC-C 模拟的是一个商品批发商的订单系统,一共九张表:仓库、区域、客户、历史订单、新订单、订单头、订单行、商品、库存。压测时五类事务按固定比例混合执行,新订单占 45%,付款占 43%,订单查询占 4%,发货占 4%,库存查询占 4%。另外新订单和付款各有约 1% 的概率走回滚分支,模拟真实业务里的失败重试。
这五类事务的负载特征差异很大,理解这一点才能看懂后面的结果。新订单是纯写入重负载,要往订单头、订单行、新订单三张表插数据,还要更新库存表的销量和订单数,同时读商品、区域、仓库、客户。付款主要是更新,写历史表、更新区域和仓库的销售额、更新客户余额。订单查询是纯读,读客户、订单头、订单行。发货最重,要先删新订单、更新订单头、更新订单行的发货时间、更新客户余额,属于典型的范围删加更新。库存查询最轻,读一下库存和订单行算个聚合。
为什么值得掰扯这些?因为后面你看到某个事务类型拖后腿、或者一致性校验里事务占比不对,根子往往就在这几张表的索引设计或者锁竞争上,而不是工具本身有问题。
1.2 两个可执行程序各管一段,别混着用
编译出来的第一个程序负责装载数据,参数里要指定主机、端口、库名、账号、密码、仓库数。仓库数是核心变量,一个仓库大约对应 100MB 上下的数据量(含索引,随引擎和页大小浮动),10 个仓库差不多 1GB 出头,100 个仓库就是 10GB 级别。装载过程是单线程串行的,吭哧吭哧往库里灌,机器不行的话几个仓库就能耗掉半小时。
第二个程序就是压测运行器,除了连接参数,还要给并发连接数、预热时长、正式跑多久、报表间隔。这里有个细节很多人第一次会懵:运行器的连接数不是"线程数"的概念,它开的是真实的数据库连接,每个连接内部模拟一个终端,跑完一个事务接着跑下一个,连接之间互不共享。所以并发连接数直接对应数据库侧看到的活跃会话数。
两个程序的连接参数格式是一样的,也正因如此,端口写错导致的失败会同时出现在两个阶段,容易让人误以为是自己灌数据的方式有问题。
1.3 端口问题的真正来源,比想象中多
端口连不上,绝大多数情况不是网络问题,而是下面这几种:
第一种,大写-P和小写-p搞混了。-P是端口,-p是密码。这两个参数在视觉上几乎没区别,尤其是从网页上复制命令的时候,稍微一改就串了。你把-P 3307写成-p 3307,工具会很开心地把3307当成密码,然后连默认端口,报的错跟端口无关,是认证失败或者连不上。
第二种,主机名写成localhost。这个坑非常隐蔽。MySQL 的 C 客户端库在解析主机时有个约定俗成的行为:主机为localhost时会优先走 Unix socket 文件,而不是 TCP。走 socket 的话,端口参数就是废的,它连的是 socket 文件对应的那个实例。你明明写了 3307,实际连的可能是 3306,或者干脆连不上。解决办法很简单,把localhost换成127.0.0.1,强制走 TCP。
第三种,服务端只监听了本地地址或者监听地址不对。配置文件里bind-address设成127.0.0.1,那你从另一台机器压测就怎么也连不上,但本机用命令行客户端一切正常,对比起来特别迷惑人。
第四种,工具版本压根不支持端口参数。这个后面单独说,是真正需要动代码的情况。
第五种,容器或者多实例环境下的端口映射。宿主机上看到的是 13307,容器里是 3306,你压测命令里写哪个,取决于你是从宿主机发起还是从容器网络内发起。这类问题排查时第一步永远是ss -lntp看一眼到底谁在听哪个端口。
2. 从编译到灌数:一套可复现的建库流程
标题里提到"测试方法",我想先把方法这条线走通。很多教程给的步骤是对的但没有解释,照着敲能跑,中间断一次就完全不知道从哪儿接。下面这套流程是我自己反复用过、也在不同机器上复现过的。
2.1 编译前必须补齐的依赖,以及 makefile 里那两个路径
源码是纯 C 写的,依赖 gcc、make 和 MySQL 的客户端开发库。不同发行版的包名不一样,基于 RPM 系的通常是mysql-devel或者mariadb-devel,基于 DEB 系的是libmysqlclient-dev或者default-libmysqlclient-dev。装完之后用mysql_config --cflags和mysql_config --libs能分别拿到头文件路径和链接参数,这两个输出是排查编译错误的关键。
官方带的 Makefile 里通常硬编码了/usr/include/mysql和-lmysqlclient。如果你的客户端库装在别的路径下,比如/usr/local/mysql/include,那编译时会报一堆mysql.h: No such file or directory。这时候有两个选择:改 Makefile 里的CFLAGS加上-I/usr/local/mysql/include,或者更省事的办法,直接把mysql_config的输出拼进去:
make CFLAGS="$(mysql_config --cflags) -O2 -Wall" LIBS="$(mysql_config --libs)"链接阶段如果报cannot find -lmysqlclient,说明库文件路径没在链接器的搜索范围里,检查一下mysql_config --libs输出里有没有-L参数,有的话原样带上就行。编译成功后会得到两个可执行文件,file一下确认是当前架构的 ELF,别稀里糊涂拷了个别的平台的过来。
还有一点,MySQL 8.0 之后默认的认证插件变了,老的客户端库可能不支持新的认证方式,表现是连接时报Authentication plugin 'caching_sha2_password' cannot be loaded。要么升级客户端库到 8.0 版本,要么给测试账号单独指定老的认证插件。测试环境里后者更快,但不建议在生产环境照做。
2.2 建表脚本里的字符集与主键长度,两个容易忽略的细节
源码目录下有个脚本目录,里面有建表、加外键和索引、删表、统计行数几个 SQL 文件。建表脚本是官方写的,字段类型和主键都是按 TPC-C 规范来的,但字符集和存储引擎这两处经常需要手动调整。
字符集默认可能是早期的 utf8,在 MySQL 8.0 上虽然还能用,但排序规则会和utf8mb4不一致,混着用的时候偶尔会出现比较行为差异。更稳妥的做法是建表前统一在脚本头部加一句SET NAMES utf8mb4;,或者干脆把建表语句里的字符集改成utf8mb4。注意主键长度,客户表的姓名字段是 16 字符,订单行和商品描述里有 24 到 50 字符的字段,用utf8mb4的话一个字符占 4 字节,最长索引前缀要留够,别因为长度超限导致建表失败。
存储引擎没什么好纠结的,TPC-C 是有事务的负载,必须用支持事务的引擎。脚本里默认就是 InnoDB,保持不动就行。但如果你在 MySQL 8.0 上跑,注意NO_ZERO_DATE这个 SQL 模式,老脚本里如果有DEFAULT '0000-00-00'之类的写法会直接报错,改掉或者临时放宽模式。
外键和二级索引是单独一个脚本,官方建议灌完数据再加,原因下一节讲。
2.3 灌数阶段的速度控制,别一上来就追求"一次灌满"
数据装载是单线程的,每张表逐行插入,速度主要受三个东西制约:服务端的刷盘策略、表上有没有二级索引和外键、以及磁盘的随机写能力。
第一条,测试环境里可以适当放宽持久性换速度。把innodb_flush_log_at_trx_commit临时设成 2,把二进制日志关掉,这两项加起来对灌数速度的提升相当可观。注意这是测试环境专属操作,灌完数据准备正式压测前要改回来,否则测出来的 tpmC 会虚高,失去参考意义。
第二条,先建表不带二级索引和外键,灌完数据再补。这个顺序上的差异不是一点点——每插入一行都要维护一堆 B+ 树,索引越多插入越慢,外键还要额外做父表的存在性检查。我做过对比,同一台机器同一份数据,带全部索引灌和裸表灌,时间能差出一倍以上。
第三条,按仓库分批灌。有的分支版本支持指定仓库区间,可以一批批往里灌,灌完一批确认一下行数再灌下一批。如果手上的版本只支持一次性指定总仓库数,那就要接受一个现实:中途断了不能续,只能清空重来。这一点踩过一次就记住了,灌到第 60 个仓库的时候机器被人重启,那个心情不解释了。所以灌大数据量之前,先在 1 个仓库的小规模上把整套流程跑通,确认脚本、账号、权限、磁盘空间都没问题,再上量。
# 先灌 1 个仓库验证流程 ./tpcc_load -h 127.0.0.1 -P 3307 -d tpcc100 -u tpcc -p 'Test@2024' -w 1 # 流程没问题后,清空重来,灌目标规模 ./tpcc_load -h 127.0.0.1 -P 3307 -d tpcc100 -u tpcc -p 'Test@2024' -w 1002.4 灌完之后必做的四件确认
数据灌完不等于可以开测了,中间还有几步不能省。
第一,核对行数。源码里有统计脚本,跑一遍看看各表行数是不是符合预期。仓库表应该等于仓库数,区域表是仓库数乘以 10,客户表是仓库数乘以 30000,订单表是仓库数乘以 30000,订单行大约是订单数乘以 10 上下浮动。对不上的话,说明中途有事务失败被吞掉了,这种数据拿去压测,一致性校验大概率过不了。
第二,把二级索引和外键加回去,加完之后再跑一次ANALYZE TABLE更新统计信息。这一步很重要,优化器没有准确的基数统计,执行计划可能选错索引,测出来的数字和你以为的完全不是一回事。
第三,确认表空间和磁盘剩余空间。100 个仓库灌完差不多 10GB,后面压测过程中还有 undo 和 redo 的增长,磁盘剩个几百兆的时候开始跑,跑到一半空间满了,那个报错会非常难定位。
第四,记下数据规模、字符集、索引情况、服务端配置。后面要对比多轮结果,这些元信息缺一项,数字就没法横向比较。
3. TPCCRunner 连接串里的端口:-P 与 -p 的致命混淆
这一节专门讲端口,因为标题里点了这个关键词,而且这确实是最高频的卡点。我把两个程序的参数、版本差异和三种改法都摆出来。
3.1 两个程序的参数对照
装载程序的常用参数:
| 参数 | 含义 | 备注 |
|---|---|---|
-h | 数据库主机 | 写127.0.0.1而不是localhost |
-P | 端口 | 大写,非默认端口必填 |
-d | 数据库名 | 需提前建好 |
-u | 用户名 | 需有建表和写入权限 |
-p | 密码 | 小写,注意和端口区分 |
-w | 仓库数量 | 决定数据规模和后续压测上限 |
压测运行器的常用参数在此基础上多了几个:
| 参数 | 含义 | 经验取值 |
|---|---|---|
-c | 并发连接数 | 从 16 起,逐步加压到 CPU 核数的 2 到 4 倍 |
-r | 预热秒数 | 不少于 60,冷缓存场景给到 300 |
-l | 正式运行秒数 | 不少于 300,短于此噪声太大 |
-i | 报表间隔秒数 | 10 到 30,看你想看多细 |
-f | 结果输出文件 | 强烈建议带上,屏幕滚没了就没了 |
-n | 是否跳过一致性检查 | 一般不要跳 |
这几个参数没有哪个是可有可无的。预热时间太短,测出来的是缓存冷启动状态下的性能;运行时间太短,一次 checkpoint 抖动就能把结果带偏;不加输出文件,跑完发现关键那一屏被刷过去了,只能重跑。
3.2 大写 P 和小写 p,以及 localhost 走 socket 的真相
先说最要命的那个。命令里同时出现-P 3307和-p Passw0rd,这两个参数在解析时是独立的,不会互相干扰。但如果你手滑写成-p 3307 -p Passw0rd,工具会取最后一次出现的值当密码,端口就保持默认。反过来写成-P Passw0rd,它会试图把Passw0rd解析成端口号,得到一个 0 或者一个荒谬的值。
排查这类问题有个笨但有效的办法:把命令里的密码参数先改成肯定错的,看报错是认证失败还是连接失败。如果是认证失败,说明端口和主机是通的,问题在账号;如果是连接被拒绝,说明根本没到认证那一步,问题在端口和主机。这个二分法能省掉大量瞎猜。
再说localhost。C 客户端库在mysql_real_connect里对主机名有特殊处理:主机为 NULL 或者localhost时,走本地 socket 连接,端口参数被忽略。这意味着你把实例放在 3307,命令里写-h localhost -P 3307,实际连的是 socket 文件对应的实例,可能是 3306,也可能因为 socket 路径不匹配直接失败。改成-h 127.0.0.1就强制走 TCP,端口参数立刻生效。这条经验适用范围很广,几乎所有基于 MySQL C 客户端库的工具都有这个行为。
3.3 手上版本不支持端口参数时,改源码的具体做法
有些渠道拿到的源码比较老,连接函数里端口位置写死成 0,参数解析也没有-P这一项。这种情况下只能在源码里动刀。核心就一处,找到调用连接函数的地方,通常是类似这样的代码:
if (mysql_real_connect(&mysql, host, user, password, db, 0, NULL, 0) == NULL) { fprintf(stderr, "error: %s\n", mysql_error(&mysql)); exit(1); }第五个参数就是端口,写死成 0 意思是"用默认值"。把它改成一个变量,然后在参数解析的地方加一个分支:
int port = 3306; /* 在 getopt 的选项字符串里加上 P: */ while ((c = getopt(argc, argv, "h:P:d:u:p:w:c:r:l:i:f:")) != -1) { switch (c) { case 'P': port = atoi(optarg); break; /* 其他分支略 */ } } if (mysql_real_connect(&mysql, host, user, password, db, port, NULL, 0) == NULL) { fprintf(stderr, "connect failed: %s\n", mysql_error(&mysql)); exit(1); }改完重新编译。注意这个改动要在装载程序和压测程序两边都做,因为它们是两份独立的源码文件,各有一处连接调用。只改一边的后果是灌数据能连上、压测连不上,或者反过来,排查起来更绕。
改源码还有个替代方案:如果不想动代码,可以在一台中转机上用端口转发把非标准端口映射成 127.0.0.1:3306,工具连本机默认端口就行。这个办法在临时验证时很快,但会引入额外的网络跳转和缓冲区,压测结果会有偏差,正式出报告时不要用。
3.4 密码里的特殊字符,以及配置文件到底会不会被读
密码里带@、#、$这些字符时,一定要用单引号包起来。Shell 会先把$展开、把#当成注释起始,你传给程序的密码已经不是你写的那个了。稳妥写法是-p 'P@ss#w0rd$1'。
另一个常见误解:有人以为程序会读~/.my.cnf里的[client]段,把里面的 host 和 port 带进去。MySQL 的命令行客户端确实会读,但基于 C API 自己写的程序默认不读,除非显式调用了读取配置文件的接口。所以你在my.cnf里改了端口,命令行客户端立刻生效,压测工具却完全无视,两者表现不一致,很容易误判成工具坏了。
4. 六类典型报错的定位路径与修复手法
压测跑起来之后出的错,可以按"连接层、SQL 层、结果层"三层来分。分层的好处是每一步都能把排查范围砍掉一半。
4.1 连接层:连不上、认证失败、连接数打满
连接被拒绝(错误码 111),按这个顺序查:服务进程在不在、监听端口对不对、监听地址是不是只有 127.0.0.1、防火墙有没有放行、命令里的-h和-P是不是写反了。用ss -lntp | grep 3307一眼就能看到谁在听哪个地址,比猜快得多。
认证失败,先确认账号能不能从当前主机登录。MySQL 的账号是"用户名@主机"的组合,tpcc@localhost和tpcc@127.0.0.1在权限表里是两条记录。压测机和被压机不是同一台时,要确保账号的主机部分是%或者压测机的实际地址。
连接数打满,报Too many connections。这种情况通常不是服务端max_connections太小,而是压测端开的连接数超过了预期,或者上一轮跑完的连接没释放干净。先看服务端的Threads_connected当前值,再回头核对压测命令里的-c参数。
4.2 SQL 层:值超范围、主键冲突、表不存在
Out of range value一般出现在自增主键上。TPC-C 的订单号是按区域分配的,每个区域 3000 个订单循环使用,如果区域数量和数据规模对不上,自增列可能越界。还有一种情况是老版本脚本里字段类型定义得偏小,数据量一大就顶到上限。
Duplicate entry说明有重复主键。原因多半是上一轮的数据没清干净,又在同一批主键上重新插入。每次重跑之前必须把业务表清空,别指望程序自己处理。
Table doesn't exist除了字面意思,还有一个隐蔽原因:库名大小写。Linux 上库名表名默认区分大小写,命令里写TPCC而实际库叫tpcc,就会报这个错。养成习惯,建库和命令里用完全一样的小写名字。
4.3 结果层:一致性校验不通过该怎么读
压测结束会打印一段约束检查,这是整份报告里信息量最大的部分,很多人扫一眼就过去了。它主要检查两件事:事务占比和响应时间。
事务占比检查,看五类事务的实际比例是不是接近标准值。正常的偏差在几个百分点以内。如果某类事务占比明显偏低,说明它执行得慢,被其他事务抢了时间片,或者因为锁等待卡住了。这时候要去查那类事务涉及的表有没有热点行,比如库存表在仓库维度上的争用。
响应时间检查,看每类事务的 90 分位是否在规定上限内。新订单、付款、订单查询、发货的上限都是 5 秒,库存查询是 20 秒。如果有超过的,说明系统在压力下已经出现了明显的长尾,这时候报出来的 tpmC 数字没有参考价值,先解决延迟问题再谈吞吐。
最后那个 tpmC 数字,不要孤立地看。TPC-C 规范里每个仓库的吞吐上限是 12.86 tpmC,也就是说 N 个仓库的理论最大吞吐是 12.86 乘以 N。如果你测出来的数字接近甚至超过这个上限,那不是系统太强,而是数据量不够、压力没打满,结果不可信。反过来,如果远低于上限,才是正常的调优空间。
4.4 压测中途异常中断的定位方法
跑到一半突然断了,屏幕上只有一行错误,这种情况最难受。先把输出文件拿出来,看最后一屏是什么事务类型、什么错误码。如果有死锁(Deadlock found),大概率是并发太高导致行锁争用,降低-c重试一次,能跑通就说明是并发度问题,不是数据问题。
如果是连接被重置(MySQL server has gone away),通常是服务端的wait_timeout或者max_allowed_packet触发的。压测场景下事务都是短事务,理论上不会超时,但要确认压测端有没有长时间的空闲连接。还有就是服务端 OOM 被系统杀掉,这个去看系统日志里有没有进程被终止的记录。
还有一种情况是磁盘写满。redo log 和 undo 在压测期间增长很快,尤其是在调大了并发和事务量的情况下。压测前预留的空间不要只按数据量算,至少留出数据量的两到三倍。
5. 服务端参数与客户端参数如何左右 tpmC 的绝对值
同一套硬件,参数不同,tpmC 能差出一倍。这不是夸张,是刷盘策略和日志配置直接决定的。但要注意,一味追求高数字没有意义,你要的是"这套配置在真实场景下的表现",而不是"把所有安全阀都拆掉之后的极限值"。
5.1 服务端:三个直接决定写入吞吐的开关
第一个是 redo log 的刷盘策略。设成 1 表示每次事务提交都刷盘,最安全也最慢;设成 2 表示每秒刷一次,崩溃可能丢一秒数据;设成 0 表示交给后台线程,性能最好但风险最高。TPC-C 是写密集负载,这一项对 tpmC 的影响是决定性的。测试环境里可以拿三档各跑一次,把差距量化出来,写进报告里,比一句"我们调优了"有说服力得多。
第二个是 redo log 文件大小。早期版本默认只有几十兆,高并发写入时频繁触发 checkpoint,性能曲线会呈现出规律的锯齿。调大到几百兆甚至 1G 以上,锯齿会明显平缓。8.0 之后这一项改成了动态调整,但思路是一样的——日志空间要能容纳一段时间的写入量。
第三个是双一配置里的另一个:sync_binlog。如果开了二进制日志,每次提交都要同步 binlog,这个开销不小。纯测试环境可以关掉 binlog,或者把同步策略放宽。但如果你要测的是"和生产一致"的配置,那就老老实实保持开启,接受这个损耗。
还有一个容易忽略的点:NUMA 绑定和 CPU 亲和性。数据库进程在多路机器上跨 NUMA 节点访问内存,延迟会明显上升。压测前用工具把进程绑到一个节点上,数字往往能稳定不少。
5.2 客户端:连接数、预热时长、仓库数三者的匹配关系
并发连接数不是越大越好。连接数从 16 加到 64,tpmC 可能翻倍;从 64 加到 256,可能只是持平甚至下降,因为锁争用和上下文切换吃掉了收益。我的做法是从小到大扫一遍,画出连接数-吞吐曲线,找到拐点,那个拐点对应的连接数才是这台机器的合理工作点。
预热时长的作用是让缓存热起来、让连接池稳定、让第一次 checkpoint 过去。预热太短,前几十秒的数据会明显偏低,把平均值拉下去。给到 60 秒是下限,数据量大或者存储慢的机器给到 300 秒更稳妥。
仓库数和连接的匹配有个经验比例:每 10 个仓库配 10 到 20 个连接比较合适。仓库太少而连接太多,所有连接都在抢同几行数据,锁等待会非常严重,报表里能看到某类事务的 90 分位飙到几秒。仓库够多而连接太少,压力打不满,机器闲着。
5.3 结果解读:别只盯着 tpmC 那一个数
一份完整的压测结果至少要看四个维度:吞吐、延迟分位、事务占比、资源使用率。
吞吐就是 tpmC 和每分钟新订单数,这个大家都知道。延迟分位要看 90 分位和最大值,平均值会掩盖长尾,而长尾才是用户真正感受到的卡顿。事务占比前面说过了,是判断负载是否均衡的重要依据。资源使用率要看 CPU、磁盘 IOPS、磁盘利用率和网络,如果 CPU 只用了 40% 但磁盘已经 100%,那瓶颈在存储,加 CPU 没用。
把这四项放在一起看,才能回答"这套配置能扛多少"这个问题。单看一个数字,很容易得出错误结论。
6. 重复跑批时的环境复位与结果归档
跑一轮压测不算本事,能连续跑十轮、每轮结果都可复现、可对比,才是这套方法真正落地的地方。这一节讲两个动作:复位和归档。
6.1 每轮之间到底要清什么
最少要清三样东西。第一是业务数据,把九张表按依赖顺序清空或者删了重建,注意先删子表再删父表,或者临时关掉外键检查。第二是查询缓存和缓冲池,FLUSH TABLES加上重启实例最彻底,不想重启的话至少把缓冲池清一遍,否则下一轮跑的是热缓存状态,和前一轮不可比。第三是统计信息,重新ANALYZE一遍,让优化器基于干净的数据做决策。
还有一样容易忘的:服务端的状态计数器。SHOW GLOBAL STATUS里的累计值会跨轮次累加,如果你想用Com_*这类计数器算事务量,一定要在两轮之间取差值,而不是直接读绝对值。
清完之后做一次快速校验:各表行数是不是零、缓冲池大小是不是回到初始值、账号连接数是不是归零。三步确认,花不了一分钟,能避免一整轮的返工。
6.2 一份能用的测试记录该包含哪些字段
我习惯每轮都记一张表,字段不多但每项都必要:
| 字段 | 示例 | 为什么记 |
|---|---|---|
| 轮次编号 | R07 | 后续引用和复盘 |
| 数据规模 | 100 仓库 | 决定了 tpmC 上限 |
| 并发连接 | 64 | 和吞吐一起看才有意义 |
| 预热/运行 | 120s / 600s | 短跑和长跑结果不可比 |
| 刷盘策略 | redo=2, binlog 关闭 | 直接决定写入吞吐 |
| tpmC | 8642 | 核心指标 |
| 新订单 90 分位 | 128ms | 判断长尾 |
| 一致性检查 | 全部通过 | 不通过则结果作废 |
| 异常备注 | 第 4 分钟出现一次 checkpoint 抖动 | 解释曲线上的坑 |
这张表的价值在于,后面你要回答"为什么这次比上次快了 15%"的时候,能一秒钟定位到是改了哪个参数,而不是翻聊天记录。
6.3 硬件与网络噪声,最容易被忽略的变量
虚拟化环境里跑压测,同一个配置两次结果差 20% 是常事。原因可能是宿主机上的邻居在抢 IO,可能是 CPU 频率降频,可能是网络中断合并策略不同。所以正式出报告前,用完全相同的配置连跑三次,看结果的离散程度。三次之间标准差超过 5%,那这台机器就不是一个合适的基准测试平台。
还有个小技巧:压测机和被压机不要放在同一台物理机上,否则两者抢 CPU 和内存带宽,测出来的是互相干扰的结果。网络也尽量走万兆直连,别经过多层交换和防火墙策略,否则测的可能是网络设备。
我在实际操作中的体会是,最难的不是把工具跑起来,而是建立一套"结论可复现"的习惯。同一套配置、同一份数据、同样的预热和运行时长,跑三次取中位数,把每次的资源配置和参数改动都记下来,这样出来的数字才敢拿去支撑决策。工具本身只是秤,秤准不准,取决于你有没有认真做过校准。