1. 问题背景与压测场景还原
去年双十一大促前,我们团队对秒杀系统进行了一次全链路压测。这个秒杀系统的核心流程采用业界常见的设计模式:
- 用户请求先通过Redis Lua脚本完成资格校验和库存扣减
- 生成订单事件写入Redis Stream
- 异步消费者从Stream读取消息并持久化到MySQL
压测参数设置为5,200并发请求,模拟1,000个真实用户抢购限量1,000件的商品。第一次压测跑完后,系统表面看起来运行良好:业务日志显示成功下单1,000次,Redis库存也确实扣减到了0。但核对数据库时发现了一个致命问题——实际订单记录只有999条,库存剩余1。
关键提示:这种"差1"现象在高并发系统中往往不是统计误差,而是系统性缺陷的表现。就像量子物理中的"丢失的角动量",必须找到合理解释。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 问题定位与证据链构建
2.1 四维数据对账法
我采用了四维度数据交叉验证的方法来定位问题根源:
- 压测统计报表:显示成功请求1,000次
- Redis库存记录:确认已从1,000扣减到0
- MySQL订单表:实际记录999条
- 应用错误日志:发现关键异常堆栈
通过对比发现,系统没有出现超卖(最终库存≥0),但存在Redis与MySQL之间的数据不一致。这指向异步处理链路中存在消息丢失或处理失败的情况。
2.2 日志中的关键线索
深入分析日志后,发现两类异常模式:
- 空指针异常:
java复制java.lang.NullPointerException:
at com.xxx.service.impl.VoucherOrderServiceImpl.lambda$null$0(VoucherOrderServiceImpl.java:87)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:223)
异常发生在proxy.createVoucherOrder()方法调用处
- 用户ID为空的异常订单:
code复制[WARN] 消费到异常订单:userId=null, voucherId=10001
``
