ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Lottery 抽奖系统实践:设计滑动库存编号分布式锁,处理活动秒杀场景

2026/9/26 3:45:33 拓冰建站 浏览量
Lottery 抽奖系统实践:设计滑动库存编号分布式锁,处理活动秒杀场景 文档教程后端【免费下载链接】CodeGuide:books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总旨在为大家提供一个清晰详细的学习教程侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助请给予支持(关注、点赞、分享)项目地址https://gitcode.com/gh_mirrors/code/CodeGuide点击查看免费下载导读当大量用户同时参与抽奖活动时活动本身就演变成了秒杀场景——集中化的库存扣减如果直接落在数据库上TPS 一旦达到 1k2k 就会把数据库拖垮。本文以 Lottery 分布式抽奖系统第 19 节的实现为主线讲解如何引入 Redis 替换原有的数据库行级锁方案并通过“滑动库存编号”将锁的颗粒度从活动 ID 细化到库存编号维度最后结合 MQ 异步消息完成缓存与数据库库存的最终一致性处理。读完本文你将掌握秒杀场景下从“独占锁”到“滑块锁”的演进思路、库存编号加锁的设计方法以及缓存扣减 MQ 落库的一致性落地套路。一、背景为什么活动参与需要分布式锁抽奖系统的核心链路是“领取活动 → 执行抽奖 → 落库结果”其中领取活动环节涉及活动库存的扣减。在第 19 节之前这部分库存扣减依赖的是数据库行级锁例如通过UPDATE activity SET surplus surplus - 1 WHERE activity_id ? AND surplus 0这类语句依赖数据库行锁保证不超卖。但行级锁的问题在于数据库行锁在集中热点行上会形成严重竞争TPS 到 1k2k 时数据库连接与锁等待就会把系统拖垮秒杀场景并发峰值集中、持续时间短抽奖活动通常 push 发出后 13 分钟结束数据库根本不是为这种瞬时集中写设计的。因此第 19 节的核心改造是把库存扣减的动作从数据库前移到 Redis用 Redis 分布式锁 缓存扣减处理集中化库存竞争。相关上下文可参阅 第11节声明事务领取活动领域开发原有领取活动领域的事务与库存校验逻辑以及 第12节在应用层编排抽奖过程领取活动在整体流程编排中的位置。二、环境准备引入 Redis 服务由于本章需要用到 Redis需要在云服务器或本地搭建 Redis 服务。如果暂时没有云服务器在本地搭建 Redis 也可以只是会少一些云环境的配置练习。Redis 环境就绪后在抽奖系统中引入 Redis 模块用于支撑用户参与抽奖活动的库存扣减优化。三、锁的颗粒度设计从独占锁到滑动库存编号3.1 为什么不能直接锁活动编号一个最直觉的方案是对活动编号如100001加锁这就是独占锁独占锁针对于活动 ID 加锁。但第 19 节明确指出了它的缺陷“不要把锁直接放到活动编号上这样在极端临界情况下会出现秒杀解锁失败导致库存有剩余但不能下单的情况。”独占锁的粒度是整个活动所有参与该活动的用户串行排队竞争同一把锁一方面吞吐极低另一方面在极端临界情况下锁释放与重新获取的时序问题可能造成“库存有剩余但用户无法下单”的事故。在 notes.md 的面试问答“秒杀的滑块锁讲解”中对两类锁的定位讲得很清楚独占锁是加给个人流程的——无资源竞争如贷款单受理分段/滑块/无锁化是加给库存的——有资源竞争如秒杀、商品发货等集中资源类场景。3.2 滑动库存编号加锁的设计第 19 节的优化方向是增加锁的颗粒度以滑动库存剩余编号的方式进行加锁。例如活动编号为100001库存编号则形如100001_1 100001_2 100001_3 ...每个库存编号对应一把独立的锁用户参与活动时先通过滑动窗口获取一个可用的库存编号再针对该编号加锁并执行扣减。这样多个用户可以并行抢不同编号的锁避免了独占锁在活动编号上的串行竞争锁的竞争面从“1 个活动”摊薄到“N 个库存编号”并发能力随库存量线性扩展避免了极端临界情况下独占锁释放异常导致整个活动无法下单的问题。滑块锁的核心目标是去竞态避免独占锁影响系统的整体响应性能。这也是面试高频考点“Redis 滑动库存分布式锁是如何实现的”的标准答案素材。3.3 为什么加了锁还要用 incr 扣减一个常见疑问是滑动库存编号都有了直接用incr扣减不就行了为什么还要加锁答案是加锁是兜底。你不知道什么时候会出现 incr 结果不对的情况例如Redis 集群配置问题特例出现 Redis 问题后需要恢复库存没有锁保护时并发下可能出现超卖。所以滑动库存编号锁的意义在于即使 incr 本身很快就像公共卫生间的一个坑一个门谁进去谁锁上没有就跑到下一个门也需要“锁门”来保证并发语义的正确性。四、扣减流程缓存扣减 MQ 异步落库4.1 两级库存设计第 19 节把库存扣减拆成了两个阶段缓存扣减Redis用户领取活动时直接在 Redis 中对滑动库存编号执行扣减。这一步扛住秒杀峰值流量。数据库扣减异步缓存扣减完成后数据库中的库存其实并没有扣减需要发送一条 MQ 消息来异步更新数据库中的活动库存。这一设计的核心动机MQ 天然具备消峰能力。降低 MQ 分片的情况下消费效率有所下降但不会对数据库造成压力通过异步落库保证最终数据一致性即可如果并发体量更大MQ 的消费端还可以不直接更新数据库而是先更新到缓存再由定时任务在最终阶段同步落库进一步减少对数据库表的操作。4.2 与原有流程的对比改造前的领取活动流程依赖数据库行级锁完成库存扣减参见 第11节声明事务领取活动领域开发存在并发瓶颈改造后用户领取活动 → Redis 滑动库存编号加锁去竞态 → 缓存库存扣减承载秒杀流量 → 发送 MQ 消息 → MQ 异步消费更新数据库活动库存最终一致性其中 MQ 解耦的整体思路延续自 第16节使用MQ解耦抽奖发货流程——抽奖系统把“抽奖”和“发奖”用 MQ 消息串联避免单个流程过长导致用户一直等待本节则把同一思想应用到“缓存扣减”与“数据库扣减”之间。补充与异常兜底衔接的是 第18节扫描库表补偿发货单MQ消息 中基于 xxl-job 的定时扫描补偿思路——凡是依赖 MQ 异步落库的状态都需要有任务扫描补偿机制兜底保证全流程可靠性。如果 MQ 消费失败导致数据库库存未扣减同样可以借助定时任务对账补偿。五、设计原则与工程取舍结合 notes.md 中的总结本节落地的核心取舍如下维度独占锁不推荐滑动库存编号锁本章方案锁粒度整个活动 ID单个库存编号如 100001_1竞争面全活动串行竞争按库存编号并行竞争吞吐低随并发下降高随库存量扩展风险极端临界解锁失败库存剩余但无法下单并行抢不同编号锁去竞态适用个人流程类无资源竞争集中资源类秒杀、库存工程层面的取舍还要注意响应优先对于非交易的活动类场景要的就是一个“快”——快速响应、快速释放可接受容错失败概率但不能影响主核心交易链路。营销秒杀场景的根本诉求是保证不超卖。锁不是万能incr 滑动编号锁解决的是并发扣减的正确性最终一致性靠 MQ 异步落库 定时任务兜底而不是靠强一致事务。锁的恢复场景当 Redis 出现异常需要恢复库存时分布式锁是安全兜底防止恢复过程中超卖。六、总结第 19 节完成了一次典型的秒杀场景优化改造引入 Redis把集中化库存竞争从数据库前移到缓存层从活动 ID 独占锁优化为滑动库存编号分布式锁100001_1、100001_2、100001_3…把锁颗粒度细化到库存编号去竞态、提吞吐、避免极端临界事故缓存扣减 MQ 异步更新数据库利用 MQ 消峰特性降低对数据库的压力以最终一致性取代强一致。这套“滑块锁 缓存扣减 MQ 落库”的组合是营销秒杀场景的经典解法。面试中常见的“Redis 滑动库存分布式锁如何实现”“为什么不用独占锁”“incr 就能扣库存为什么还要加锁”“缓存扣了但 MQ 没发出去怎么办”等追问都可以在 notes.md 的面试问答部分找到对应的话术与思考方向可作为复习素材结合本仓库 Lottery 抽奖系统介绍 继续深入。赞分享文档教程后端【免费下载链接】CodeGuide:books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总旨在为大家提供一个清晰详细的学习教程侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助请给予支持(关注、点赞、分享)项目地址https://gitcode.com/gh_mirrors/code/CodeGuide点击查看免费下载相关推荐在 Lottery 抽奖系统中引入 XXL-JOB分布式任务调度处理活动状态扫描在 Lottery 抽奖系统中引入 XXL JOB分布式任务调度处理活动状态扫描 本文以 Lottery 分布式抽奖系统DDD 四层架构为背景讲解如何在文档教程后端Log-Lottery3D球体互动抽奖系统的技术解析与场景实践Log Lottery3D球体互动抽奖系统的技术解析与场景实践 在2023年某互联网公司年会上传统的抽奖箱抽奖方式让现场氛围一度陷入沉闷——当主持人从纸箱中前端桌面应用抽奖系统数据库设计实战从活动、策略到分库分表的 7 组表设计Lottery 项目抽奖系统数据库设计实战从活动、策略到分库分表的 7 组表设计Lottery 项目 在基于 DDD 四层架构的 Lottery 抽奖系统中库表设计是整个项文档教程后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考