先说我吃过的一次亏。线上活动突发资损,我第一时间抓日志,产品侧查询参数里赫然写着一个 99999999;顺着调用链往下翻,同一个值出现在库存扣减、优惠券发放、消息队列的 payload 里,像病毒一样复制到了整条链路。复盘会开到最后,没有人能找到一行明显出错的代码,唯一的解释是:“这个 99999999 本来只是想当占位符用。”那次事故让我明白:一串全 9 的数字,在人的手里是“测试方便”,在系统里却可能被当成无限大的真实业务量。这篇文章想聊的,就是这类写在代码里的“99999999”占位值是怎么产生的,会造成哪些危害,以及我后来沉淀下来的一套排查和治理方法。无论你是后端开发、测试还是正在画原型的业务同学,只要你与数字字段打过交道,都值得花几分钟把这套思路带走。
1. 一次由“99999999”引发的线上事故全复盘
1.1 事故现场:兜底值变成了“发券上限”
那次事故的背景是积分商城里的一次大促活动。产品提出的需求是“给指定用户发一张不限制库存的优惠券”,也就是发券时不设置库存上限。负责实现的同学把“不限制库存”直接翻译成了一个数字:99999999。代码大概长这个样子:
java复制public Optional<Integer> getCouponStock(String campaignId) {
CampaignConfig config = configClient.get(campaignId);
if (config == null || config.getStock() == null) {
// 读不到就默认无限量
return Optional.of(99999999);
}
return Optional.of(config.getStock());
}
单独看这段代码,最坏也就是当一个活动没配置好时,把库存当成了无穷大。问题在于,这个“库存”不是只给一个接口用。下游至少有三个系统会拿到它:一个负责生成券,一个负责扣减库存,一个负责做风控限流。三个系统的判断逻辑都非常朴素,比如“库存大于 0 就能继续发”“扣减次数小于限制就放行”。当三个地方同时看到 99999999 时,它们都会以为前方有整片草原,于是开始放心大胆地发。
真正的触发点,是运营在新建活动时漏配了一个 key。按照代码作者的预期,漏配时 99999999 会兜住“不限量”,但因为没有配套的库存预检,也没有单独的“是否限量”开关,这个兜底值把所有下游校验全部绕开了。于是线上出现了第一批“不限制领取次数”的券,用户用脚本反复刷,库存从 1000 被一路扣成负数,最后资损报表亮红的时候,负责该领域的开发才知道出事了。
那次复盘给我留下的第一个冲击是:我们总以为线上事故一定对应一行明显写错的代码,可实际里罪魁祸首往往是一串本来“非常合理”的数字。9 是最大的十进制数字,99999999 看起来就是“很大很大”,所以它被当作“无上限”的快捷表达,非常自然。
