15158846557 在线咨询 在线咨询
15158846557 在线咨询
所在位置: 首页 > 营销资讯 > 网站运营 > Redis秒杀实战:微信抢红包(附源码)

Redis秒杀实战:微信抢红包(附源码)

时间:2023-05-18 09:03:02 | 来源:网站运营

时间:2023-05-18 09:03:02 来源:网站运营

Redis秒杀实战:微信抢红包(附源码):

一、导读

为啥写这个微信抢红包项目呢,公司 0202 年 08 月 22 日,公司周年庆,抢了100多红包 ,O(∩_∩)O哈哈~

二、微信抢红包实现原理

2.1 业务流程分析


2.2 功能拆解


2.2.1 新建红包

在 DB、Redis 分别新增一条记录

2.2.2 抢红包(并发)

请求Redis,红包剩余个数,大于0才可以拆,等会0时,提示用户,红包已抢完

2.2.3. 拆红包(并发)

「用到技术」

Redis 中数据类型的 String 特性的原子递减(DECR key)和减少指定值(DECRBY key decrement)

「业务」

  1. 请求 Redis ,当剩余红包个数大于 0,红包个数原子递减,随机获取红包
  2. 计算金额,当最后一个红包时,最后一个红包金额=总金额-总已抢红包金额
  3. 更新数据库
2.2.4. 查看红包记录

查询 DB 即可

2.3 数据库设计

红包流水表

CREATE TABLE `red_packet_info` ( `id` int(11) NOT NULL AUTO_INCREMENT, `red_packet_id` bigint(11) NOT NULL DEFAULT 0 COMMENT '红包idtimestamp+5位随机数', `total_amount` int(11) NOT NULL DEFAULT 0 COMMENT '红包总单位分', `total_packet` int(11) NOT NULL DEFAULT 0 COMMENT '红包总个数', `remaining_amount` int(11) NOT NULL DEFAULT 0 COMMENT '剩余红包单位', `remaining_packet` int(11) NOT NULL DEFAULT 0 COMMENT '剩余红包个数', `uid` int(20) NOT NULL DEFAULT 0 COMMENT '新建红包户的户标识', `create_time` timestamp COMMENT '创建时间', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATECURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='红包信息新建个红包插⼊⼀条记录';红包记录表

CREATE TABLE `red_packet_record` ( `id` int(11) NOT NULL AUTO_INCREMENT, `amount` int(11) NOT NULL DEFAULT '0' COMMENT '抢到红包的', `nick_name` varchar(32) NOT NULL DEFAULT '0' COMMENT '抢到红包的户的', `img_url` varchar(255) NOT NULL DEFAULT '0' COMMENT '抢到红包的户的头像', `uid` int(20) NOT NULL DEFAULT '0' COMMENT '抢到红包户的户标识', `red_packet_id` bigint(11) NOT NULL DEFAULT '0' COMMENT '红包idtimestamp+5位随机数', `create_time` timestamp COMMENT '创建时间', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATECURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='抢红包记录表个红包插⼊⼀条记录';2.4 发红包 API

发红包接口开发

❝往db中就单纯存入一条记录,Service层和Mapper层,就简单的一条sql语句,主要是提供思路,下面会附案例源码,不要慌
2.5 抢红包 API

抢红包算法拆解

通过上图算法得出,靠前面的人,手气最佳几率小,手气最佳,往往在后面

  1. 发 100 元,共 10 个红包,那么平均值是 10 元一个,那么发出来的红包金额在 0.01~20 元之间波动
  2. 当前面 4 个红包总共被领了 30 元时,剩下 70 元,总共 6 个红包,那么这 6 个红包的金额在 0.01~23.3 元之间波动
抢红包接口开发

2.6 测试

2.6.1. 发红包

2.6.2 模拟高并发抢红包(Jmeter压测工具)

因为我发了 10 个红包,金额是 20000,使用压测工具,模拟50个请求,只允许前10个请求能抢到红包,并且金额等于20000。

三、布隆过滤器(重要)

3.1 介绍

布隆过滤器是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

3.1.1 优点

相比于其他的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。布隆过滤器存储空间和插入/查询时间都是常数。另外三列函数相互之间没有关系,方便由硬件并行实现。布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。

3.1.2 缺点

但是布隆过滤器的缺点和有点一样明显。误算率是其中之一。随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表足矣。

3.2 布隆过滤器有什么用

  1. 黑客流量攻击:故意访问不存在的数据,导致查程序不断访问DB的数据
  2. 黑客安全阻截:当黑客访问不存在的缓存时迅速返回避免缓存及DB挂掉
  3. 网页爬虫对 URL 的去重,避免爬取相同的URL地址
  4. 反垃圾邮件,从数十亿个垃圾邮件列表中判断某邮件是否垃圾邮件(同理,垃圾短信)
  5. 缓存击穿,将已存在的缓存放到布隆中,当黑客访问不存在的缓存时迅速返回避免缓存及 DB 挂掉

四、布隆过滤器实现会员转盘抽奖

4.1. 需求

一个抽奖程序,只针对会员用户有效

4.2 通过google布隆过滤器存储会员数据

  1. 程序启动时将数据放入内存中
  2. google自动创建布隆过滤器
  3. 用户ID进来之后判断是否是会员
4.3 代码实现

4.3.1 引入依赖

<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>29.0-jre</version></dependency>4.3.2 数据库会员表

CREATE TABLE `sys_user` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `user_name` varchar(11) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '⽤户名', `image` varchar(11) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '⽤户头像', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;4.3.3 初始化布隆过滤器

dao 层和 dao 映射文件,就单纯的一个 sql 查询,看核心方法,下面会附源码滴,不要慌好嘛

4.3.4 控制层

4.3.5 测试

4.4 缺点

  1. 内存级别产部
  2. 重启即失效
  3. 本地内存无法用在分布式场景
  4. 不支持大数据量存储

五、Redis布隆过滤器

5.1 优点

  1. 可扩展性 Bloom 过滤器
  2. 不存在重启即失效或定时任务维护的成本
5.2 缺点

  1. 需要网络IO,性能比基于内存的过滤器低
5.3 布隆过滤器安装

5.3.1. 下载

github:https://github.com/RedisBloom/RedisBloom

链接: https://pan.baidu.com/s/16DlKLm8WGFzGkoPpy8y4Aw
密码: 25w1
5.3.2. 编译

make

5.3.3 将 Rebloom 加载到 Redis 中

先把 Redis 给停掉!!!在 redis.conf 里面添加一行命令->加载模块

loadmodule /usr/soft/RedisBloom-2.2.4/redisbloom.so5.3.4 测试布隆过滤器

六、SpringBoot 整合 Redis 布隆过滤器

6.1 编写两个lua脚本

  1. 添加数据到指定名称的布隆过滤器
  2. 从指定名称的布隆过滤器获取key是否存在的脚本
local bloomName = KEYS[1]local value = KEYS[2]--bloomFilterlocal result_1 = redis.call('BF.ADD',bloomName,value)return result_1local bloomName = KEYS[1]local value = KEYS[2]--bloomFilterlocal result_1 = redis.call('BF.EXISTS',bloomName,value)return result_16.2 在 RedisService.java 中添加 2 个方法

6.3 验证

七、秒杀系统设计

7.1 秒杀业务流程图

7.2 数据落地存储方案

  1. 通过分布式redis减库存
  2. DB存最终订单信息数据
7.3 API性能调优

  1. 性能瓶颈在高并发秒杀
  2. 技术难题在于超卖问题
7.4 实现步骤

提前将秒杀数据缓存到 redis

set skuId_start_1 0_1554045087 --秒杀标识set skuId_access_1 12000 --允许抢购数set skuId_count_1 0 --抢购计数set skuId_booked_1 0 --真实秒杀数
  1. 秒杀开始前,skuId_start为0,代表活动未开始
  2. 当skuId_start改为1时,活动开始,开始秒杀叭
  3. 当接受下单数达到sku_count*1.2后,继续拦截所有请求,商品剩余数量为0(为啥接受抢购数为1万2呢,看业务流程图,涉及到“校验订单信息”,一般设置的值要比总数多一点,多多少自己定)
利用 Redis 缓存加速增库存数

"skuId_booked":10000 //从0开始累加秒杀的个数只能加到1万将用户订单数据写入 MQ(异步方式)。

另外一台服务器监听 mq,将订单信息写入到 DB。

好了,以上就是完整的开发步骤,下面我们开始编写代码

7.5 代码实战

7.5.1. 网关浏览拦截层

1、先判断秒杀是否已经开始

2、利用 Redis 缓存 incr 拦截流量

7.5.2. 订单信息校验层

1、校验当前用户是否已经买过这个商品

2、校验通过直接返回抢购成功

7.5.3 开发lua脚本实现库存扣除

1、库存扣除成功,获取当前最新库存

2、如果库存大于0,即马上进行库存扣除,并且访问抢购成功给用户

3、考虑原子性问题

4、返回抢购结果

控制层

Service 层

布隆过滤器

7.6 初始化redis缓存

set skuId_start_1 0_1554045087 --秒杀标识set skuId_access_1 12000 --允许抢购数set skuId_count_1 0 --抢购计数set skuId_booked_1 0 --真实秒杀数7.7 秒杀验证

jmeter 配置

压测秒杀验证原子性

八、项目下载

链接(奶牛 快传): https://cowtransfer.com/s/74998eaf64da44
取件码: rqzbyj

九、尾声

演示的时候,我使用的 Redis 单机的,吞吐量不是很大,感兴趣的,可以自己搭建个 Redis 主从复制+哨兵+集群,然后再测试。

最近比较忙,没时间完善微信抢红包秒杀的原子性。下面那个完整案例抢库存的,亲自使用 Jmeter 压测几次,是原子性的,可以拿来借鉴,感兴趣的同学,可以借鉴下面抢库存的代码,把微信抢红包的功能在完善下,我就不修改啦。

作者:陈彦斌
链接:https://www.cnblogs.com/chenyanbin/p/13587508.html
来源:博客园

关键词:实战

74
73
25
news

版权所有© 亿企邦 1997-2025 保留一切法律许可权利。

为了最佳展示效果,本站不支持IE9及以下版本的浏览器,建议您使用谷歌Chrome浏览器。 点击下载Chrome浏览器
关闭