红包雨该如何设计?(完结)
2018年,王思聪的冲顶大会,西瓜视频的百万英雄,再到映客的芝士超人,直播答题火爆全网。
我服务的一家电商公司也加入了这次热潮,技术团队研发了直播答题功能。答题结束之后,红包会以红包雨的形式落下,用户点击屏幕上落下的红包,若抢到红包,红包会以现金的形式进入用户账户。
红包雨是一个典型的高并发场景,短时间内有海量请求访问服务端,技术团队为了让系统运行顺畅,抢红包采用了基于 Redis + Lua 脚本的设计方案。

1 整体流程
我们分析下抢红包的整体流程 :

- 运营系统配置红包雨活动总金额以及红包个数,提前计算出各个红包的金额并存储到 Redis 中;
- 抢红包雨界面,用户点击屏幕上落下的红包,发起抢红包请求;
- TCP 网关接收抢红包请求后,调用答题系统抢红包 dubbo 服务,抢红包服务本质上就是执行 Lua 脚本,将结果通过 TCP 网关返回给前端;
- 用户若抢到红包,异步任务会从 Redis 中 获取抢得的红包信息,调用余额系统,将金额返回到用户账户。
2 红包 Redis 设计
抢红包有如下规则:
- 同一活动,用户只能抢红包一次 ;
- 红包数量有限,一个红包只能被一个用户抢到。
如下图,我们设计三种数据类型:
- 运营预分配红包列表 ;

队列元素 json 数据格式 :
{
//红包编号
redPacketId : '365628617880842241'
//红包金额
amount : '12.21'
}
- 用户红包领取记录列表;

队列元素 json 数据格式:
{
//红包编号
redPacketId : '365628617880842241'
//红包金额
amount : '12.21',
//用户编号
userId : '265628617882842248'
}
- 用户红包防重 Hash 表;

抢红包 Redis 操作流程 :
- 通过 hexist 命令判断红包领取记录防重 Hash 表中用户是否领取过红包 ,若用户未领取过红包,流程继续;
- 从运营预分配红包列表 rpop 出一条红包数据 ;
- 操作红包领取记录防重 Hash 表 ,调用 HSET 命令存储用户领取记录;
- 将红包领取信息 lpush 进入用户红包领取记录列表。
抢红包的过程 ,需要重点关注如下几点 :
- 执行多个命令,是否可以保证原子性 , 若一个命令执行失败,是否可以回滚;
- 在执行过程中,高并发场景下,是否可以保持隔离性;
- 后面的步骤依赖前面步骤的结果。
Redis 支持两种模式 : 事务模式 和 Lua 脚本,接下来,我们一一展开。
3 事务原理
Redis 的事务包含如下命令:
| 序号 | 命令及描述 |
| 1 | MULTI 标记一个事务块的开始。 |
| 2 | EXEC 执行所有事务块内的命令。 |
| 3 | DISCARD 取消事务,放弃执行事务块内的所有命令。 |
| 4 | WATCH key [key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 |
| 5 | UNWATCH 取消 WATCH 命令对所有 key 的监视。 |
事务包含三个阶段:
- 事务开启,使用 MULTI , 该命令标志着执行该命令的客户端从非事务状态切换至事务状态 ;
- 命令入队,MULTI 开启事务之后,客户端的命令并不会被立即执行,而是放入一个事务队列 ;
- 执行事务或者丢弃。如果收到 EXEC 的命令,事务队列里的命令将会被执行 ,如果是 DISCARD 则事务被丢弃。
下面展示一个事务的例子。
redis> MULTI
OK
redis> SET msg "hello world"
QUEUED
redis> GET msg
QUEUED
redis> EXEC
1) OK
1) hello world
这里有一个疑问?在开启事务的时候,Redis key 可以被修改吗?

在事务执行 EXEC 命令之前 ,Redis key 依然可以被修改。
在事务开启之前,我们可以 watch 命令监听 Redis key 。在事务执行之前,我们修改 key 值 ,事务执行失败,返回 nil 。

通过上面的例子,watch 命令可以实现类似乐观锁的效果 。
4 事务的ACID
4.1 原子性
原子性是指:一个事务中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
第一个例子:
在执行 EXEC 命令前,客户端发送的操作命令错误,比如:语法错误或者使用了不存在的命令。
redis> MULTI
OK
redis> SET msg "other msg"
QUEUED
redis> wrongcommand ### 故意写错误的命令
(error) ERR unknown command 'wrongcommand'
redis> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
redis> GET msg
"hello world"
在这个例子中,我们使用了不存在的命令,导致入队失败,整个事务都将无法执行 。
第二个例子:
事务操作入队时,命令和操作的数据类型不匹配 ,入队列正常,但执行 EXEC 命令异常 。
redis> MULTI
OK
redis> SET...未能定位到原文的划线
企业级Agent工作流编排项目PaiFlow
Vibe Coding版本的PaiAgent
派聪明RAG AI知识库Java版本+Go版本
微服务 PmHub、技术派、MYDB
求职派JobClaw(OpenClaw/Hermes架构
PaiCLI(类似Claude Code的Agent
派简历(代码已完成)
等实战项目。
1. 微信扫右侧的优惠券加入知识星球
2. 解锁星球的实战项目教程和源码: 项目源码+教程获取
真诚点赞 诚不我欺
回复