易抽奖Pro系统的测试报告,我是上周五下班前填上最后一笔的。结论很简单:可以上线,但必须灰度放量,而且在放量头半小时要盯紧两个指标——奖品订单对账差异和数据库连接池水位。这不是一份走过场的验收文档,而是我们测试组连着做了三轮功能测试、四轮压测、两轮安全专项后整理出来的完整记录。
易抽奖Pro系统本质上是一个面向营销活动的H5抽奖平台,运营在后台配置活动时间、奖品库存、中奖概率,用户通过手机端页面参与抽奖、查看中奖记录并领取奖品。系统分为管理后台、抽奖API服务和奖品发放服务三大块。接下如果你要接手类似系统的测试,这篇报告里的测法、踩过的坑、压测数据怎么解读,基本都能直接参考。
1. 这次测试的范围、目标与一套可复现的测试环境
接手任务之后我做的第一件事不是写用例,而是把需求文档按业务流程拆开。因为"抽奖系统"这四个字看起来简单,实际一跑起来会发现它根本不是单一功能,而是一条很长的链路。
1.1 需求拆解:抽奖系统比看上去多出好几个闭环
抽奖如果只测抽奖按钮,那上线多半要出事故。我把易抽奖Pro拆成五个闭环:
- 活动配置闭环:运营创建活动、设置奖品、配置概率、审核上架、定时上下线。
- 用户参与闭环:用户身份校验、抽奖次数获取与扣减、抽奖结果返回。
- 抽奖引擎闭环:概率计算、随机数生成、奖品选择、中奖记录落库。
- 库存与发放闭环:奖品库存扣减、发放通知、奖品领取、发货状态更新。
- 对账与审计闭环:中奖流水、库存变动记录、操作日志、异常补偿。
测试的重点放在"抽奖引擎"和"库存发放"上,因为这两个环节直接关系到数据正确性。概率配置是运营在后台填的,测试要验证的是:运营填0.1%的一等奖,系统是不是真的按照0.1%的概率发出去;奖品只剩最后一件时,系统是不是真的只给一个人。这是抽奖系统最核心的价值。
1.2 测试环境与数据准备
测试环境完全按照生产拓扑搭了一套隔离环境:3个应用节点、独立的数据库、缓存集群、消息队列各一套。测试数据是专门构造的,和真实用户数据完全隔离。
- 造了10万个注册用户,覆盖普通用户、新用户、老用户、异常状态用户。
- 造了20个活动模板,包含未开始、进行中、已结束、奖品为空、概率不合法等状态。
- 奖品库存准备大约3000件,包括实物、虚拟券、谢谢参与三类。
- 压测用的账号、库存数据单独建库,避免和功能测试相互污染。
这里有个很小的细节但很关键:测试环境里要关掉非必要定时任务。比如我们有一次发现对账任务在压测过程中跑了起来,结果把压测数据当异常数据处理了,整轮数据直接作废。后来定了个规矩,压测前必须先检查环境里的定时任务状态。
1.3 分层测试方法与优先级规则
测试方法上,我没有只依赖手工点页面,而是分了三层:
- 接口自动化脚本覆盖核心链路,包括抽奖、中奖、发放、领取全流程,每次回归先跑一遍冒烟脚本。
- UI手工测试负责真机场景,比如中奖弹窗、奖品领取页面、活动倒计时、不同屏幕尺寸的展示。
- 性能和安全部分单独设计用例,用开源压测工具和故障注入手段做专项验证。
缺陷分级参考常见规则:P0是阻断上线的问题,必须立即修复;P1是重要功能异常或数据正确性问题;P2是一般功能缺陷;P3是体验性、展示性问题。每一轮测试结束,都要重新跑一遍基线用例,确保修复本身没有把别的功能改坏。
2. 抽奖核心链路的功能验证:从常规用例到"钻空子"场景
功能测试是整份报告的基础。刚开始我想得很简单:抽奖就是点一下按钮。但真正写用例的时候才发现,围绕着抽奖动作,各种边界条件和业务规则加起来超过200条用例。
2.1 常规流程用例:从抽奖次数到中奖记录
常规流程的用例主要验证这些场景:
- 活动已开始且配置正确,用户可以正常抽奖。
- 未登录用户点击抽奖,会提示登录并在登录后回到抽奖页。
- 抽奖次数扣减和抽奖结果在同一请求中返回,避免先扣次数但没返回结果。
- 中奖后生成中奖记录,奖品进入个人中心,用户收到通知。
- 未中奖时正常消耗次数,不生成任何领取入口。
里面有一个很容易忽略的点:抽奖次数扣减和结果返回必须是一致的。如果系统先扣次数、再调抽奖引擎,结果引擎超时,就有可能出现次数扣了但用户没拿到结果。这种问题在正常流程里不一定暴露,但一压测或者一弱网就会出现。所以我们专门设计了一组"扣次数成功、返回结果失败"的用例,来验证系统有没有补偿机制。
2.2 并发扣减与超卖:第一个P0缺陷就是在这里抓到的
抽奖系统最怕的不是抽不到,而是多发。我设计了一组并发场景:同一个活动只剩最后一件奖品,50个用户同时抽,期望只有一个人拿到;同一个用户在1秒内连续发起5次抽奖请求,期望只有一次生效。
第一次执行就抓到P0缺陷:并发抽奖时竟然发出了两件奖品,数据库里库存直接变成负数。这个问题的根因其实很典型:先查一次库存,判断大于0就执行扣减,再发起发放。两个操作之间没有原子性,两个并发请求都查到库存是1,于是都通过了判断。
修复方式是典型的"先扣后发"思路:把判断库存和扣减库存做成一个原子操作,扣减成功的人才允许进入发放流程;发放失败或者超时,必须回滚扣减。修复之后我把并发量提到100个用户同时抢最后一件奖品,连续跑了100次,0超卖,数据库也没有出现负库存。
2.3 概率公平性:先跑500万次模拟,再谈配置是否可靠
概率配置很容易测,难的是验证系统真按概率执行了。我从运营配置里挑了一个典型活动:一等奖0.1%、二等奖1%、三等奖15%、谢谢参与83.9%。如果只是手工点几十次,根本得不到有效结论。
我写了一个独立脚本,调用抽奖引擎500万次,每次记录抽到的奖项,最后统计分布。结论是通过的:一等奖出现次数约为4993次,和理论期望值约5000次偏差很小;二等奖约49982次,也在可接受范围。另外我做了拟合优度检验,结果没超出统计显著性的拒绝域。
除了统计结果,还要检查随机数生成的方式。我们发现某些节点重启之后,前几百次抽奖结果出现可预测的重复序列,原因是随机数种子初始化逻辑用了固定值。修复之后重新跑了一遍模拟,确认分布恢复正常。概率公平性测试绝对不能靠界面点按完成,一定要独立于接口测试做大量样本验证。
2.4 边界与"钻空子":过期活动、负数次数、重复领取
这段主要是测试人跟系统"对着干"的过程。我列了下面几个必须验证的边界场景:
- 活动开始前和刚结束后的抽奖请求必须被拒绝,即使缓存里的活动配置还没更新。
- 同一账号在抽奖次数被扣成负数之后,能不能继续提交抽奖。
- 通过修改客户端参数绕过前端按钮,直接把抽奖接口调起来。
- 奖品库存为0时,系统还能不能返回中奖结果。
- 发放通知重复发送时,用户能不能重复领取同一个中奖记录。
这些场景里发现的问题基本都集中在P1级别,有一部分在开发阶段就被拦住了,有一部分是测试阶段才暴露出来的。比如"活动结束后仍然能抽奖"这个问题,就是因为活动状态只存在缓存里,缓存刷新不及时,用户直接调接口绕过了前端校验。修复思路是每次抽奖请求都同时校验数据库里的活动时间,缓存只做加速,不做最终裁决。
3. 性能压测踩过的坑:缓存击穿、连接池耗尽与限流配置
功能没问题之后,压测才是真正让人紧张的部分。营销抽奖的活动特征是短时间高流量,用户一窝蜂涌进来,系统扛不扛得住,不压测永远不知道。我们一共做了四轮压测,前两轮都在踩坑,第三轮才拿到像样的数据。
3.1 压测模型和线程梯度怎么设计
压测模型不是随便把线程数拉高就行,而是先想清楚业务特征。抽奖场景读多写少:用户进页面、刷活动配置、查自己有没有次数,这些都是读操作;真正触发抽奖落库的请求占比很小。所以脚本里大约90%的请求是查询活动配置和用户状态,10%是实际抽奖请求。
线程梯度设计成五档:50、100、300、500、1000并发,每档持续5分钟。先跑低并发是为了摸清基线,再逐步加压找到系统的拐点。目标也很明确:系统支撑5000TPS以上,P99响应时间低于300毫秒,错误率低于0.1%,全程不允许出现超卖和重复发放。
3.2 第一轮压测就翻车:活动配置缓存过期击穿数据库
第一轮压测进行到300并发档位时,指标突然崩了:TPS从3200掉到1200,错误率冲到8.6%,监控里数据库连接池被打满,大量请求排队等待连接。
刚开始我以为是并发太高,查下来才发现真正原因是缓存击穿。活动配置的缓存时间是统一的,同一时间过期之后,大量请求同时回源数据库查活动配置。与此同时,奖品库存的扣减逻辑也在查数据库,两条链路叠加起来直接把数据库拖垮。
这里有个值得记住的结论:高并发本身不可怕,可怕的是高并发和缓存失效窗口正好撞在一起。平时单看数据库每秒几千次查询没问题,但所有请求同时打过来,再强悍的连接池也会被压穿。
3.3 修复与验证:本地缓存、分布式锁、异步发放
修复方案针对性很强:
- 活动配置缓存增加随机过期时间,避免大范围同时失效。
- 在缓存失效后加分布式锁,只允许一个请求回源数据库重建缓存,其余请求等待。
- 增加本地二级缓存,进一步降低回源频率。
- 奖品库存扣减改成在缓存里做原子操作,扣减成功的流水再异步写入数据库。
- 数据库连接池上限适当调大,同时设置了等待队列,避免无限堆积。
修复后的压测数据确实好看了很多:
| 并发线程数 | 修复前TPS | 修复后TPS | 修复后P99 | 错误率 |
|---|---|---|---|---|
| 50 | 980 | 1050 | 92ms | 0 |
| 100 | 2100 | 2300 | 131ms | 0 |
| 300 | 1200 | 3500 | 176ms | 0.01% |
| 500 | 1400 | 5100 | 214ms | 0.02% |
| 1000 | 限流拦截 | 5400 | 238ms | 0.05% |
500和1000并发档位已经触发了限流策略,所以在高并发下TPS没有无限增长。但错误率很低,说明限流策略在工作,而不是系统被打挂了。这也是压测报告里要讲清楚的一点:被限流拦住的请求不代表系统故障,反而证明保护机制确实生效。
3.4 关于限流、P99和性能结论的解读
以前有人只盯着平均响应时间,这是新手最容易犯的错误。平均时间会被大量低延迟请求拉低,真正影响体验的是P99,也就是最慢的那1%请求。用户抽奖是高频交互,卡上几百毫秒就有明显感知,所以这次压测把P99作为核心指标。
限流配置我也专门验证过。当前配置是单用户每秒最多1次抽奖请求,单IP每秒最多20次,全局令牌桶每分钟放行10万次。压测结论是性能基本达标,但上线必须配合限流和熔断,否则遇到超出预期的流量峰值,单靠扩容也来不及。压测结束后还做了一轮尖峰冲击:1000并发持续30秒,验证系统在极限情况下的恢复能力。结果没有雪崩,队列消费正常,没有超卖。
4. 兼容性、安全与故障注入:测试报告里最容易缺的三块内容
很多人做抽奖系统的测试,功能跑通了、压测也过了,就觉得可以上线。但我在几次项目里吃过兼容性和安全问题的亏,所以这次专门留了时间做专项测试。
4.1 兼容性矩阵:老WebView、弱网和不同屏幕
易抽奖Pro的前端是H5页面,用户可能在任何一款手机浏览器、微信、App内置WebView里打开。我整理了一个兼容性矩阵,覆盖iOS和安卓系的老中青三代机型、不同屏幕分辨率、不同系统WebView版本。
最典型的问题出现在老版本WebView上:抽奖转盘的CSS3动画会卡顿,甚至旋转中心偏移,用户看到的指针和实际中奖结果对不上。这种问题在普通浏览器里完全复现不出来,最后是通过降级方案解决的:检测到低版本WebView时直接隐藏动画,展示静态结果页,保证信息准确优先于视觉效果。
弱网场景也是用网络模拟器做的测试。把网络切换成高延迟、高丢包环境,抽奖请求经常超时,会出现用户点击一次,前端超时重试,后端实际收到多次请求的情况,如果没有幂等机制就直接变成多次抽奖。
4.2 安全测试:越权、重放、参数篡改与中奖记录保护
安全专项我重点关注四类问题:
- 水平越权:把请求里的用户ID替换成另一个用户,看能不能查看或领取对方的奖品。这里抓到过一个P1缺陷:奖品发放接口没有校验中奖记录归属,只要知道记录ID就能帮别人领奖。
- 重放攻击:把同一次抽奖请求原样再发一遍,看会不会再次生成中奖记录。幂等键在这里起了关键作用,服务端根据用户、活动、时间戳生成幂等ID,重复请求直接返回第一次的结果。
- 参数篡改:修改接口里的奖品ID、金额字段、概率字段,验证服务端是否信任了前端传入的参数。正确做法是服务端从配置中心取奖品的真实属性,不拿请求里的金额当最终值。
- 未授权访问:遍历管理员接口,验证管理后台的鉴权是否完整。
凡是涉及用户个人信息的测试,数据在测试结束后要做脱敏清理,不能把测试账号的真实信息流到其他环节。
4.3 故障注入:进程被杀、缓存断连、事务悬挂
故障注入是容易被忽略但非常值得做的测试。我设计了几类故障场景:
- 抽奖请求刚扣完库存、中奖流水还没落库时,直接把应用进程杀掉,验证重启后中奖记录是否能通过补偿任务补齐,已经扣掉的库存是否回滚。
- 缓存节点上的活动配置被手动删除,验证是否只能有一个请求去回源重建。
- 数据库连接中断,验证系统有没有熔断和降级,而不是无限重试把服务拖死。
- 消息队列积压,验证发放延迟是否会导致多发、漏发。
这里暴露出来的问题很有代表性:补偿任务没有独立的幂等表,中奖记录被补偿了两次,结果一个用户收到两个同样的奖品。修复后在补偿逻辑里增加幂等校验,再重复跑故障场景,最终确认补偿只执行一次。这类故障注入如果不上线前做,线上出问题就只能事后补数据,代价会大得多。
5. 缺陷统计、回归结论与上线放量建议
完整测试流程走完,我把所有问题汇总成清单。第一轮和第二轮测试集中在功能和安全,第三轮回归在压测修复之后持续跑了两天,最后一轮结论是易抽奖Pro系统可以进入上线流程。
5.1 缺陷分布与严重级别统计
先看缺陷分布:
| 功能模块 | 用例数 | 缺陷数 |
|---|---|---|
| 活动配置管理 | 125 | 18 |
| 抽奖引擎 | 280 | 31 |
| 库存与发放 | 185 | 24 |
| 用户与中奖记录 | 150 | 16 |
| 安全与权限 | 95 | 12 |
总计835条用例,缺陷101个。按严重级别看:P0缺陷1个,P1缺陷8个,P2缺陷46个,P3缺陷46个。那个P0就是奖品超卖,已经在第二轮回归中确认修复。P1缺陷基本完成修复并回归通过,其中2个P1问题最终被评估为受限于当前版本架构的已知限制,已在报告中单独说明。
5.2 回归测试结论与上线放量建议
三轮回归通过率分别是92.6%、98.1%和99.2%。遗留的3个P3缺陷都不影响主流程,属于文案、样式和极端情况下的提示优化。结论是:可以上线,但强烈建议灰度放量。
放量方案我写得很具体:先放5%的用户,观察30分钟,重点看奖品对账差异、缓存命中率、数据库连接池水位和错误率;如果一切正常,再放20%、50%,最后全量。任何一步如果出现对账差异超过万分之三,立即暂停抽奖并回滚到旧版本。灰度期间每小时跑一次中奖流水与库存变动对账,确保两边的数字完全一致。
5.3 如果再测一次,我会提前做的几件事
这次项目做完,我有几个很实际的心得想记录下来。
第一,测试之前一定要先和开发把"概率、库存、流水"这三张表的字段含义和流转关系对齐,画一张数据流图,再开始写用例。很多缺陷不是因为逻辑复杂,而是测试人员对数据如何流转不清楚。
第二,压测应该更早介入,不要等功能全部完成才启动。我们第一轮压测时功能还在修复中,导致压测数据附带了功能bug的噪声,花了不少时间排查。
第三,中奖流水和奖品发放从设计阶段就必须考虑幂等。只要涉及并发、超时、重试,没有幂等就会出重复数据,这是抽奖系统绕不过去的坎。
第四,所有测试数据在上线前必须清理干净。测试环境里的活动、奖品、账号如果被带回生产,会影响线上对账,这个细节越早知道越好。