去年冬天,某电商大促前夜。压测报告一片绿,TPS标到峰值的一点五倍,所有人松了口气。结果零点开卖,数据库连接池先炸了。不是流量超了,是压测那套数据和生产的真实分布,压根是两回事。
这事我记了很久。不是因为它多离奇,而是它太典型了——性能压测场景设计与数据构造,往往是整个压测链条上最敷衍的一环。工具用得再溜,脚本写得再花,源头歪了,全白搭。

场景不是把并发数往上怼
很多团队做压测,上来就问:目标TPS多少?然后JMeter线程组一拉,完事。说实话,这跟拿把尺子量水温差不多——方向就不对。
真实的用户行为是有时间分布和业务比例的。早上八点刷商品详情的多,晚上八点下单支付的多。你压测时候把所有接口按一比一比一混合,那是理想国,不是生产环境。我见过一个团队,压测只压了下单接口,没压购物车和优惠券计算,结果大促当天优惠券服务CPU打到百分之九十八,下单链路跟着雪崩。
所以场景设计的第一步,是把核心链路拆出来,按真实业务比例加权。比如浏览详情、加购、领券、下单、支付,这五个动作在生产日志里占比是 50:20:15:10:5,那你压测脚本就得按这个比例来。别拍脑袋。
还有个坑——思考时间。有人为了把压力打上去,直接把思考时间设成零。兄弟,那是DDoS,不是用户行为。真实的用户会停顿、会犹豫、会切出去回个微信再回来。这些停顿累积起来,对服务端的连接保持和内存占用影响巨大。
数据构造的深浅,决定压测真假
再说数据。压测数据造得假,比场景设计歪更致命。
我见过最离谱的,是拿生产库导出一份,把用户名手机号脱敏了,其他字段原封不动。看起来没毛病对吧?但生产数据是有冷热分层的。热门商品可能就那几百个SKU,占了八成流量;冷门商品几万个,几乎没人看。你压测时候随机从几万个SKU里取,缓存命中率低得可怜,数据库压力反而比生产小。因为你压根没压到热点上。
正确的做法是:按真实二八分布造数据。热门SKU只造一百个,但让八成请求都落在这上面。冷门的造几万个,占两成流量。这样才能复现真实的缓存穿透和热点竞争。

还有关联关系。订单表、用户表、商品表,外键约束下你造数据得讲顺序。我习惯用存储过程或者Python脚本按依赖顺序批量造,先生成用户,再生成地址,再生成订单,最后生成支付流水。别图省事用CSV硬导,外键对不上,跑一半报错,那个烦躁劲儿,谁干谁知道。
另外说说字段长度。生产环境里用户昵称有长有短,有表情符号,有生僻字。你压测数据全用“test001”这种定长字符串,数据库的页分裂和存储引擎行为跟生产能一样吗?造数据要尽量模拟真实字段的分布特征,长度、字符集、空值比例,都得顾及。
几招踩过坑才明白的土办法
第一,压测数据要能清理。造的时候爽,跑完几百G垃圾数据留在测试库,第二天同事跑来骂人。我习惯给每条压测数据打上批次标记,比如batch_20231201,跑完一条SQL按批次删,干净利落。
第二,别在压测库上建太多索引。有些人为了压测跑得快,给所有字段加索引,结果生产环境索引少,一压就慢,你根本发现不了问题。压测库的索引结构应该跟生产保持一致,哪怕慢,也得忍着。
第三,数据构造脚本要版本化。今天造一批,明天造一批,参数改来改去,最后没人记得哪批数据对应哪个场景。用Git管起来,跟代码一样,每次压测报告里写清楚用的哪版数据脚本,哪个批次号。别嫌麻烦,出事的时候这就是救命稻草。
最后说个心态问题。性能压测场景设计与数据构造,是个慢活。它没有写业务代码那种立竿见影的成就感,但它的价值在每一次大促平稳度过里。你糊弄它,它就糊弄你,而且是在最要命的时候。
那次翻车之后,我们团队定了个规矩:压测前,场景和数据构造方案必须过评审。谁写的脚本,谁来讲清楚数据分布怎么来的,比例怎么算的。讲不清楚,回去重写。听起来有点较真,但从此再没出过那种“压测全绿、生产全挂”的糟心事。
值了。