# redis秒杀系统 **Repository Path**: tzmoxi/redis-flash-killing-system ## Basic Information - **Project Name**: redis秒杀系统 - **Description**: 本项目主要是模拟redis的解决Session共享问题,在秒杀场景下的使用,例如如何解决缓存穿透,缓存雪崩,缓存击穿问题,实现秒杀问题,防止超卖问题,如何实现签到,共同关注等问题。 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2023-06-24 - **Last Updated**: 2023-11-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # redis秒杀系统 ## 介绍 本项目主要是redis的应用,模拟redis是如何解决集群session不能共享问题,第二解决高并发问题,基于缓存穿透问题解决恶意进行高并发请求不存在的值,使用redis集群或者使用随机TTL解决缓存雪崩问题,对于热点问题如何进行预热等等,后续内容持续更新 ## 技术说明 SpringBoot MybatisPlus Mysql5.7以上 Redis Nginx ## 1.使用教程 ### 后端文件使用说明 1. 下载源码,导入idea中 2. 修改配置文件application.yaml的数据库密码和redis密码,改成自己的密码 修改数据库和redis密码 ![img.png](image/img1.png) 3. 导入数据库文件hmdp.sql 4. 以上都完成,运行src/main/java/com/hmdp下的HmDianPingApplication程序入口文件,点击运行,在浏览器输入http://localhost:8081/shop-type/list即可打开页面,说明安装成功 ### 前端文件使用说明 1. 将文件nginx文件放到一个目录中,本项目提供的nginx的前端文件放在了html目录下了 2. 在nginx所在目录下打开命令行窗口,使用命令start nginx.exe启动前端 3. 打开浏览器,开启检查,调到手机模式,然后访问http:localhost:8080即可看到页面 ![img.png](image/img.png) ## 模块说明 1. 使用redis解决session共享问题 2. 缓存更新策略 3. 缓存三兄弟解决方案(缓存穿透,缓存雪崩,缓存击穿) ## 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request # 2.模块开发说明 ## 2.1基于session实现登陆注册功能 ### 总的需求分析 用户输入用户名(即手机号)点击发送验证码,用户收到验证码填写验证码信息点击登陆,如果验证码正确,则登陆成功,登陆成功以后,在此登陆的基础上在进行新的请求,都需要进行拦截校验。 ### 总的需求实现分解 1.点击发送验证码是一个请求 2.点击登陆也是一个请求, 3.再有新的请求,对请求进行拦截,校验身份
也就是说一个登陆注册功能总共包括了三个请求,接下来对这个登陆注册功能进行详细的流程设计 #### 发送验证码需求分析 用户输入手机号,点击发送验证码,后台接收到请求的信息,手机号,首先第一步是判断手机号是否符合规则,使用正则表达式进行校验,如果正确,则生成验证码,存储验证码到session,最后发送短信接受验证码 流程实现: ![img.png](image/img2.png) #### 点击登陆需求分析 用户填写完接收到的验证码,点击登陆按钮,把信息以表单的形式提交到后台,后台首先校验手机号是否正确,如果正确再校验验证码是否正确,如果正确接着去数据库中查询用户信息,如果没有查询到用户信息进行注册,最终把用户信息存储到session中。具体的流程实现如下所示 ![img_1.png](image/img_3.png) #### 定义拦截器校验登陆状态 登陆成功之后,在该用户的基础上,进行其它控制层的数据请求的时候,首先需要进行拦截,判断该用户是否存在,如果存在的话,把用户信息保存到ThreadLocal线程池中,然后进行放行,如果用户没有登陆,则拦截进行登陆,实现流程如下 ![img.png](image/img4.png) ## 2.2基于redis实现登陆注册模块开发 ### 总的需求分析 用户输入用户名(即手机号)点击发送验证码,用户收到验证码短信提醒,把收到的短信填写到完毕,点击登陆,成功登陆跳转到首页,然后再点击其它的请求,就是在该用户的基础上进行查询 ### 总的需求实现分解 1.点击发送验证码是一个请求 2.点击登陆也是一个请求, 3.再有新的请求,对请求进行拦截,校验身份 也就是说一个登陆注册功能总共包括了三个请求,接下来对这个登陆注册功能进行详细的流程设计 ### 发送验证码需求分析 用户进入登陆页面,填写手机号,然后点击发送验证码,后端拿到封装的手机号参数,首先第一步进行手机号的校验,使用正则表达式进行校验,如果手机校验通过,则进行下一步,生成验证码,把验证码存储到redis中,键用手机号表示,值用生成的验证码,为了防止key可其它业务产生冲突,需要定义一个见名知意的前缀进行说明,验证码存储完毕之后,然后下一步就是发送验证码到手机短信进行提醒,发送验证码整个流程就到此结束。具体的流程分析见下图。 ![image.jpg](image/image5.jpg) ### 点击登陆需求分析 用户填写好收到的验证码,首先校验手机号是否正确,然后再校验验证码是否一致,以上信息都通过,根据手机号查询用户信息,把查询到的用户信息存储到Redis中,如果没有查询到就进行注册,注册完毕之后,同样保存到redis中键以uuid存储,值以用户信息存储,最后把token返回给前端,每次进行新的请求都会携带这个token进行拦截校验,通过就放行 ![image6.jpg](image/image6.jpg) ### 拦截器拦截校验登陆状态 拦截校验用户登陆成功以后,如果在此链接的基础上,进行新的请求,每次请求都会携带登陆成功颁发的token指令进行请求,进行新请求首先会被拦截器进行拦截,在拦截器中,首先是获取token,然后根据token去redis中获取用户信息,根据获取到的用户信息进行判断,如果为空的话,直接拦截让他登陆,否则的话,就直接进行下一步,把查询到的信息存储到ThreadLocal,供其它请求使用。执行的流程如下所示 ![image7.jpg](image/image7.jpg) ### 解决访问静态页面不拦截不能够刷新时长问题 用户访问首先的页面,不能够进行拦截,也就不会自动进行时长的刷新,解决的方案需要进行两次拦截,第一次拦截所有,刷新时长,第二次只拦截判断是否登陆。 ![image8.jpg](image/image8.jpg) ## 3.添加缓存逻辑分析 用户进行数据请求,首先去redis缓存中去查询,如果查询到,直接返回命中结果,否则的话去数据库中去查询数据,把查询到的数据直接放进redis中,并且返回结果 ![image10.jpg](image/image10.jpg) ## 4.缓存更新策略 业务需求上,对于低一致性要求,使用内存淘汰机制,例如店铺的类型,对于高一致性的需求,需要进行主动更新,并以超时剔除作为兜底放哪,例如店铺详情查询的缓存。 缓存更新策略包括三种策略,第一种是内存淘汰策略,第二种是超时踢出策略,第三种是主动更新策略 首先来介绍一下三种缓存更新策略,然后再具体比较一下,内存淘汰策略是利用Redis的内部淘汰机制,这种方式特点是数据一致性较差,但是没有任何的维护成本,超时踢出,就是给缓存数据添加TTL时间,到期后缓存就会被自动删除,这种方式的特点就是数据的一致性一般,维护成本也低,第三种就是主动更新,这种方式就需要编写业务逻辑,在修改数据库的同时,更新缓存,这种方式数据性的一致性较高,但是维护成本也高。 对于主动更新主要包括三部分,第一是Cache Aside Pattern:由缓存的调用者,在更新数据库的同时更新缓存,第二是Read/Write Through Pattern:缓存与数据库整合为一个服务,由服务统一维护一致性,调用者调用该服务,无需关心缓存一致性问题,第三Write Behind Caching Pattern:吊用这个和只操作缓存,由其它线程异步的将缓存的数据持久化到数据库,保证最终的一致性.一般是使用第一种,有缓存的调用者,在更新数据库的同时跟更新缓存。操作缓存和数据库时同时有三个问题需要考虑,第一是删除缓存还是更新缓存,第二如何保证缓存与数据库的操作是同时成功或者失败,第三是先操作缓存还是先操作数据库。 ### 删除缓存还是更新缓存 更新缓存:每次更新数据库都更新缓存,无效写操作较多 ❌ 如果更新数据库的次数多,而读取的次数较少,则每次更新时,都会增加无效的更新缓存操作。
删除缓存:更新数据库时让缓存失效,查询时再更新缓存 ✔️ ### 如何保证缓存和数据库的操作是同时成功或者失败 单体系统:使用事务注解@Transactional 分布式系统: ### 先操作缓存还是先操作数据库 先删除缓存,再操作数据库:❌
先操作数据库,再删除缓存:✔️ ## 5.缓存穿透问题 分析:缓存穿透问题在Redis和数据库都没有查到,然后攻击者恶意使用不存在的值,疯狂进行请求,导致数据库压力过大,导致宕机。
解决方案:一般的解决方案有以下几种,第一种是存储空值,第二种方式是使用布隆过滤技术进行实现。第三种增强id的复杂性,避免猜测出id的规律,第四种做好数据的基础格式校验,主要讲解前两种
存储空值实现简单,维护方便,但是会消耗额外的内存空间,例如,攻击者随机使用id进行请求,这样每次的请求都不一样,第二个可能就是造成数据的不一致。
使用布隆过滤技术,占用内存较少,没有多余的key,缺点实现起来较复杂,而且存在误判的可能。 ## 6.缓存雪崩问题 分析:缓存雪崩是指在同一时间段大量缓存的key同时失效或者Redis宕机,导致大量请求打到数据库,带来巨大压力 解决方案:1.给不同的key的TTL添加随机值,2.利用Redis集群提高服务的可用性,3.给缓存业务添加降级限流的措施,4.给业务添加多级缓存. ## 7.缓存击穿问题 问题分析:缓存击穿问题也叫热点key问题,突然key失效,导致大量的并发打到数据库上,导致数据库宕机。 解决方案:1.利用互斥锁,2.使用逻辑过期