原文链接:https://www.cnblogs.com/chenyanbin/p/13587508.html

已原创授权

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

业务流程分析

功能拆解

新建红包

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

抢红包(并发)

「使用技术」

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

「业务」

  1. 请求 Redis ,当剩余红包个数大于 0,红包个数原子递减,随机获取红包

  2. 计算金额,当最后一个红包时,最后一个红包金额=总金额-总已抢红包金额

  3. 更新数据库

「查询红包记录」

查询 DB 即可

数据库设计

红包流水表

CREATE TABLE `red_packet_info` (`id` int(11) NOT NULL AUTO_INCREMENT,`red_packet_id` bigint(11) NOT NULL DEFAULT 0 COMMENT '红包id,采⽤
timestamp+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 UPDATE
CURRENT_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 '红包id,采⽤
timestamp+5位随机数',`create_time` timestamp COMMENT '创建时间',`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
CURRENT_TIMESTAMP COMMENT '更新时间',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='抢红包记
录表,抢⼀个红包插⼊⼀条记录';

发红包 API

发红包接口开发

  • 新增一条红包记录

  • 往 mysql 里面添加一条红包记录

  • 往 redis 里面添加一条红包数量记录

  • 往redis里面添加一条红包金额记录

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

抢红包 API

  • 抢红包功能属于原子减操作

  • 当大小小于 0 时原子减失败

  • 当红包个数为0时,后面进来的用户全部抢红包失败,并不会进入拆红包环节

  • 抢红包功能设计

    • 将红包ID的请求放入请求队列中,如果发现超过红包的个数,直接返回

  • 注意事项

    • 抢到红包不一定能拆成功

抢红包算法拆解

img

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

  1. 发 100 元,共 10 个红包,那么平均值是 10 元一个,那么发出来的红包金额在 0.01~20 元之间波动

  2. 当前面 4 个红包总共被领了 30 元时,剩下 70 元,总共 6 个红包,那么这 6 个红包的金额在 0.01~23.3 元之间波动

抢红包接口开发

「测试」

「发红包」


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

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

布隆过滤器

介绍

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

优点

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

缺点

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

布隆过滤器有什么用

  1. 黑客流量攻击:故意访问不存在的数据,导致查程序不断访问DB的数据

  2. 黑客安全阻截:当黑客访问不存在的缓存时迅速返回避免缓存及DB挂掉

  3. 网页爬虫对 URL 的去重,避免爬取相同的URL地址

  4. 反垃圾邮件,从数十亿个垃圾邮件列表中判断某邮件是否垃圾邮件(同理,垃圾短信)

  5. 缓存击穿,将已存在的缓存放到布隆中,当黑客访问不存在的缓存时迅速返回避免缓存及 DB 挂掉

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

需求

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

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

  1. 程序启动时将数据放入内存中

  2. google自动创建布隆过滤器

  3. 用户ID进来之后判断是否是会员

代码实现

引入依赖

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>29.0-jre</version>
</dependency>

数据库会员表

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;

初始化布隆过滤器

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

控制层

测试

缺点

  1. 内存级别产部

  2. 重启即失效

  3. 本地内存无法用在分布式场景

  4. 不支持大数据量存储

Redis布隆过滤器

优点

  1. 可扩展性 Bloom 过滤器

  2. 不存在重启即失效或定时任务维护的成本

缺点

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

布隆过滤器安装

「下载」

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

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

「编译」

make

「将 Rebloom 加载到 Redis 中」

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

loadmodule /usr/soft/RedisBloom-2.2.4/redisbloom.so

「测试布隆过滤器」

SpringBoot 整合 Redis 布隆过滤器

编写两个lua脚本

  1. 添加数据到指定名称的布隆过滤器

  2. 从指定名称的布隆过滤器获取key是否存在的脚本

local bloomName = KEYS[1]
local value = KEYS[2]
--bloomFilter
local result_1 = redis.call('BF.ADD',bloomName,value)
return result_1

local bloomName = KEYS[1]
local value = KEYS[2]
--bloomFilter
local result_1 = redis.call('BF.EXISTS',bloomName,value)
return result_1

在 RedisService.java 中添加 2 个方法

验证

秒杀

秒杀业务流程图

数据落地存储方案

  1. 通过分布式redis减库存

  2. DB存最终订单信息数据

API性能调优

  1. 性能瓶颈在高并发秒杀

  2. 技术难题在于超卖问题

实现步骤

提前将秒杀数据缓存到 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。

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

代码实战

网关浏览拦截层

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

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

  • 用 incr 方法原子加

  • 通过原子加帕努单当前 skuId_access 是否达到最大值

订单信息校验层

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

  • 需要存储用户的uid

  • 存数据库效率太低

  • 存Redis value方式数据太大

  • 存布隆过滤器性能高且数据量小(推荐)

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

开发lua脚本实现库存扣除

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

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

3、考虑原子性问题

  • 保证原子性的方式,采用 lua 脚本

  • 采用lua脚本方式保证原子性带来缺点,性能有所下降

  • 不保证原子性缺点,放入请求量可能大于预期

  • 当前扣除库存场景必须保证原子性,否则会导致超卖

4、返回抢购结果

  • 抢购成功

  • 库存没了,抢购失败

控制层

Service 层

布隆过滤器

初始化redis缓存

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

秒杀验证

jmeter 配置

压测秒杀验证原子性

项目下载

链接: https://pan.baidu.com/s/1hZUPRAljkqO05fYluqJBhQ  密码: 1iwr

尾声

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

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

特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:

长按订阅更多精彩▼如有收获,点个在看,诚挚感谢

Redis 秒杀实战相关推荐

  1. Spring Boot+Vue/前后端分离/高并发/秒杀实战课程之spring Security快速搭建oauth2 内存版身份认证

    Springboot快速搭建oauth2 内存版身份认证 环境准备 点击[Create New Project]创建一个新的项目 项目环境配置 配置Thymeleaf 搭建oauth2认证,加入两个依 ...

  2. 高并发核心编程Spring Cloud+Nginx秒杀实战,秒杀业务的参考实现

    秒杀业务的参考实现 本节从功能入手重点介绍Spring Cloud秒杀实战业务处理的3层实现:dao层.service层.controller层. 秒杀的功能模块和接口设计 秒杀系统的实现有多种多样的 ...

  3. 你真的懂Spring Cloud+Nginx秒杀实战,Nginx高性能秒杀和限流吗?

    Nginx高性能秒杀和限流 从性能上来说,内部网关Zuul限流理论上比外部网关Nginx限流的性能会差一些.和Zuul一样,外部网关Nginx也可以通过Lua脚本的形式执行缓存在Redis内部的令牌桶 ...

  4. Spring Boot Redis Cluster 实战干货

    转载自  Spring Boot Redis Cluster 实战干货 添加配置信息 spring.redis:database: 0 # Redis数据库索引(默认为0)#host: 192.168 ...

  5. Redis 秒杀案例

    Redis 秒杀案例 文章目录 Redis 秒杀案例 实现 ab工具模拟并发 超卖和超时问题解决 配置JedisPool连接池来解决超时问题 利用乐观锁淘汰用户,解决超卖问题 库存遗留问题解决 什么是 ...

  6. JAVA电商秒杀实战(三)

    JAVA电商秒杀实战(三)- - 实现第三方登录 QQ登录 短信验证码登录 QQ登录 首先导入所需要的相关依赖: <dependency><groupId>org.apache ...

  7. 保姆级redis6全流程学习和redis秒杀全流程

    文章目录 前言 一.redis解决的问题 1.1.1解决session共享问题 1.1.2降低io读操作 1.2.1nosql数据库的特点 1.2.2 NoSQL的适用场景 1.2.3 NoSQL不适 ...

  8. 大数据Spark “蘑菇云”行动第76课: Kafka+Spark Streaming+Redis项目实战

    大数据Spark "蘑菇云"行动第76课:   Kafka+Spark Streaming+Redis项目实战 jedis插件 redis <dependency>   ...

  9. Redis数据实战之GEO在LBS中应用与自定义新数据类型

    Redis数据实战之GEO在LBS中应用与自定义新数据类型 引言 面向 LBS 应用的 GEO 数据类型 GEO 的底层结构 GeoHash 的编码方法 如何操作 GEO 类型 如何自定义数据类型 R ...

最新文章

  1. 工具栏对象GUI Status 与GUI Title
  2. Android --- RecycleView 中默认选择第一个 item,点击第 2 个 item 时改变其字体样式,其余的 item 的字体样式恢复默认
  3. nrf51822-主从通信分析1
  4. linux时间格式怎么写,linux基础--时间格式
  5. 生成技术在人工智能平台中的应用探索
  6. 二级VB培训笔记10:知识点串讲
  7. 以太坊2.0存款合约地址余额28.87万ETH,进度达55%
  8. grep命令_「Linux」- ps -ef |grep 命令
  9. ado.net mysql 转义_ADO.NET数据库查询
  10. 北理工java分析题_2020春北理工《Java技术与应用》在线作业-1(参考)
  11. 视频播放插件 Video.js
  12. JAVA数据库访问控制框架设计与使用
  13. win7打开xp共享的计算机,xp/win7局域网共享软件
  14. 微信小程序–二维码生成器
  15. 百度飞桨7日营——世界冠军带你从零实践强化学习总结
  16. 地图选择区域遮罩层自定义控件实现
  17. Uncaught SyntaxError: Unexpected token < in JSON at position 0 at JSON.parse (<anonymous>)
  18. 快乐大掌柜源码--快乐大掌柜系统开发模式介绍
  19. Mothur1_Mothur的简介及运行准备
  20. C++ vector数组实现多级排序—使用sort()函数

热门文章

  1. poj1002(map的使用)
  2. poj1410(线段相交问题判断)
  3. Codeforces Round #648 (Div. 2)题解 A-D
  4. wamp安装和配置_Joomla安装教程
  5. 《题目与解读》红书 训练笔记目录《ACM国际大学生程序设计竞赛题目与解读》
  6. 洛谷 2 月月赛 I 『MdOI R4』 (Div2) A ~ D 四题全,也许会有六题,超高质量题解 (Div.1E、F下辈子一定补)【每日亿题2 / 9】
  7. 【常用技巧精选】尺取法
  8. 解题报告 - 牛客练习赛63 C - 牛牛的揠苗助长(货仓选址+二分)
  9. 0x41.数据结构进阶 - 并查集
  10. 【牛客每日一题】tokitsukaze and Soldier 题目精讲 贪心、优先队列、堆