秒杀系统高并发优化实战(C++ / Drogon):4.7 基线性能测试(官方 JMeter QPS≈371 + 阶段一总结)
4.1~4.6 把基础秒杀的下单链路(列表 / 详情 / 下单 / 不超卖 / 不重复 / 超卖路线抉择)全部跑通并验证了正确性。这一篇给阶段一打一个可量化的基线:到底能扛多少 QPS、延迟长什么样、超卖/重复到底有没有。基线是后面每一次优化的"参照物"——没有它,你根本说不清"阶段二缓存预扣减到底快了多少"。
配套资产:
scripts/jmeter-baseline.sh(双通道压测)、jmeter/seckill-baseline.jmx(官方 JMeter 脚本)、jmeter/out/report/index.html(HTML 报告)。压测在 WSL(i7-14650HX + 本地 MySQL)由真机跑出。
一、为什么必须先打基线
整个项目的验收硬指标是:每一阶段 QPS 提升一个量级 + 不超卖 + 不重复下单。阶段一(v0.1.x)是"Drogon + MySQL 直连"的最朴素形态,它的任务不是冲吞吐,而是:
- 把正确性钉死:不超卖、不重复下单,端到端可复现地证明;
- 建立延迟与吞吐基线:给后续阶段一个对比锚点(每步优化都应显著拉高 QPS、压低延迟)。
所以基线的验收标准反过来也简单:QPS 是多少不重要,0 超卖 + 0 重复必须先达成,再记录数字。
二、压测怎么搭:双通道 + 两种模式
scripts/jmeter-baseline.sh 设计成双引擎和两种模式,是为了在不同环境都能出数字、且互相校验。
两种模式
baseline:库存置1e8(60s 内绝不会售罄),专测"卖出"事务路径的稳态 QPS 与延迟。correct:库存置10、100 并发抢,核对不超卖(要求sold == orders == 10)。
双通道(引擎优先级)
- 装有官方 Apache JMeter(非 apt 缝合怪包)时,走
JMETER_BIN指向的二进制,出 HTML 报告; - 否则回退零依赖 curl 并发 harness(
timeout+xargs -P+curl -w,awk 算 QPS/百分位),保证任何机器都能出基线。
WSL 实测踩过的三个坑(已在脚本里修掉):① Java 把
localhost解析成 IPv6::1,而 Drogon 只监听 IPv40.0.0.0:8080→ 脚本统一改127.0.0.1;② 本机http_proxy被 Java 当http.proxyHost路由到代理端口 → 跑 JMeter 前unset代理 +JVM_ARGS关代理;③ 官方 JMeter 5.6.x 默认 HTTP 实现HTTPHC4Impl在 WSL 下静态初始化失败 →.jmx采样器显式implementation=Java走 JDKHttpURLConnection,功能等价、正常出样本。
三、基线结果
官方 JMeter(走 implementation=Java),100 线程 / ramp 10s / 持续 60s 打 /api/seckill:
| 指标 | 官方 JMeter 实测 | 说明 |
|---|---|---|
| 样本数 | 22352 | 实际发出的请求 |
| 吞吐 QPS | 371.03/s(≈371) | 阶段一基线 |
| 错误率 | 0% | 0 超卖、0 失败 |
| mean / median | 246.8 / 251 ms | 平均与中位延迟 |
| p90 / p95 / p99 | 333 / 359 / 399 ms | 尾延迟 |
| max | 436 ms | 最慢一次 |
curl harness 交叉验证(口径一致、独立实现):QPS≈341.3,avg 0.2888s / p50 0.2736s / p95 0.3752s / p99 0.4847s;200=20479、409=0、失败=0;MySQL sold == orders == 20492(0 超卖)。两套 harness 差约 9%,属并发模型差异,量级一致,相互背书。
correct 模式铁证(不超卖):100 并发抢 10 件库存 → 返回 10×200 + 90×409,MySQL 核对 sold=10 / orders=10。原子 UPDATE ... WHERE stock>0 + uk_user_sku 幂等在端到端层面实锤生效。
四、根因:为什么卡在 ~371 QPS
基线 ~371 不是偶然,根因很明确:每个请求都占一条 DB 连接,并持有 seckill_sku 这一行的行锁直到事务提交。
所有请求在 seckill_sku 这一行上排队过行锁,吞吐被锁的持有时间(含 MySQL 往返)严格限制——这正是"DB 直扣"方案的共性代价,也是阶段二(Redis 预扣减)、阶段三(MQ 异步)、阶段四(Lua 原子)要逐一拆掉的靶心。基线的意义就在这里:它把"哪里慢"标得清清楚楚。
五、阶段一总结
阶段一代码侧 + 压测侧已全部闭环。落地清单:
| 模块 | 内容 | 关键设计 |
|---|---|---|
| 基础秒杀 | 列表 / 详情 / 下单 | UPDATE ... WHERE stock>0 防超卖;事务内扣库存+落订单;ON DUPLICATE KEY UPDATE + uk_user_sku 幂等 |
| 登录鉴权 | 注册 / 登录 / 登出 | PBKDF2 加盐哈希;自实现 HS256 JWT;Redis 可吊销会话(sess:{jti} SETEX + 吊销 DEL) |
| 短信验证码 | 发送 / 校验 / 加固 | 腾讯云 API 3.0 直连(TC3-HMAC-SHA256 自签);Lua 原子校验 + 每日上限 + 试错上限 |
| 登录加固 | 失败计数 / 临时锁定 | Redis 共享失败计数 + Lua 原子阈值 + 临时锁定 |
| 应用层锁 | 在途闸门 | InflightGuard mutex / 自旋 / 原子三后端,DB 之前挡掉并发重复(4.8 详测) |
架构骨架(Drogon + MySQL 直连):
- 控制器只做协议转换(HTTP ↔ 业务参数),业务逻辑下沉到
service/; - 统一 JSON 响应经
src/util/HttpJson.h收敛; - Redis 缺失时登录/短信返 503,秒杀主链路不受影响(fail-open);
/api/seckill暂不接 token 校验(阶段一为压测便利,鉴权接入留待前端阶段)。
验收对照:不超卖(sold==orders,correct 模式 10==10)✅;不重复下单(uk_user_sku 兜底,orders 恒=1)✅;基线 QPS≈371 / 0 超卖 ✅。三项目标全达成。
小结
- 阶段一基线:官方 JMeter QPS≈371 / p95≈359ms / 0 超卖;curl harness 交叉验证 ≈341,量级一致、互相背书。
- 不超卖在 correct 模式实锤:
100 抢 10 → 10×200 + 90×409,sold==orders==10。 - 根因:每个请求持有
seckill_sku单行锁到事务提交 → 行锁串行化 = 吞吐瓶颈,正是后续阶段的优化靶心。 - 阶段一验收三目标(不超卖 / 不重复 / 基线可量化)全部达成,代码侧 + 压测侧闭环。🐾

