秒杀业务分析:
一、什么是秒杀
就是商家发布一些活动,使得商品降价,用户在同一时间内大量抢购商品。例如双十一、双十二购物节。
秒杀场景特点:
(一)、秒杀时大量用户会在同一时间同时进行抢购,网站瞬时访问流量激增。
(二)、秒杀一般是访问请求数量远远大于库存数量,只有少部分用户能够秒杀成功。
(三)、秒杀业务流程简单,下单,减少库存。
可能遇到的技术点:
(一)、限流
(二)、削峰
(三)、异步处理
(四)、超卖问题
(五)、分布式锁
二、秒杀方案设计
(一)、核心思想:
运用缓存,减少数据库访问的瞬间压力。
(二)、设计图
1、数据库设计
单独设计秒杀数据库
2、秒杀url设计
将秒杀的url实现动态化,也可以进行加密
3、秒杀页面静态化
将商品的描述、参数、成交记录、图像、评价等全部写入到一个静态页面,用户请求不需要通过访问后端服务器,不需要经过数据库,直接在前台客户端生成,这样可以最大可能的减少服务器的压力。
4、redis集群
采用哨兵模式,提高redis性能和可用性。
5、OpenResty/Ngint
OpenResty 是一个高性能的web服务器,大大提升了并发能力。
6、redis预减库存
7、限流
(1)、前端限流
用户在秒杀按钮点击以后发起请求,那么在接下来的5秒是无法点击(通过设置按钮为disable)。
(2)、同一用户限流
通过redis的键过期策略。
(3)、令牌桶算法限流
令牌桶算法的基本思路是每个请求尝试获取一个令牌,后端只处理持有令牌的请求,生产令牌的速度和效率我们都可以自己限定,guava提供了RateLimter的api供我们使用。
8、异步下单(RabbitMQ)
为了提升下单的效率,并且防止下单服务的失败。需要将下单这一操作进行异步处理。最常采用的办法是使用队列,队列最显著的三个优点:异步、削峰、解耦。这里可以采用rabbitmq,在后台经过了限流、库存校验之后,流入到这一步骤的就是有效请求。然后发送到队列里,队列接受消息,异步下单。下完单,入库没有问题可以用短信通知用户秒杀成功。假如失败的话,可以采用补偿机制,重试。
9、服务降级(Hystrix)
假如在秒杀过程中出现了某个服务器宕机,或者服务不可用,应该做好后备工作。
三、超卖问题
(一)、什么是超卖问题
超卖问题,这里是指多⼈抢购同⼀商品的时候,多⼈同时判断是否有库存,如果只剩⼀个,则都会判断有库存,此时会导致超卖现象产生,也就是⼀个商品下了多个订单的现象。
(二)、超卖问题解决方案
使用分布式锁
(三)、分布式锁应该具备条件
- 在分布式系统环境下,⼀个⽅法在同⼀时间只能被⼀个机器的⼀个线程执行;
- 高可用的获取锁与释放锁;
- 高性能的获取锁与释放锁;
- 具备可重入特性;
- 具备锁失效机制,防止死锁;
- 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。
(四)、分布式锁实现的几种方式
1、基于数据库实现方式----数据库排他锁
在查询语句后⾯增加 for update。
2、基于Redis实现方式
(1)获取锁的时候,使⽤setnx加锁,并使⽤expire命令为锁添加⼀个超时时间,超过该时间则⾃动释
放锁,锁的value值为⼀个随机⽣成的UUID,通过此在释放锁的时候进⾏判断。
(2)获取锁的时候还设置⼀个获取的超时时间,若超过这个时间则放弃获取锁。
(3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执⾏delete进⾏锁释放。
3、基于ZoopKeeper实现方式
(1)创建⼀个目录mylock;
(2)线程A想获取锁就在mylock目录下创建临时顺序节点;
(3)获取mylock⽬录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺
序号最小,获得锁;
(4)线程B获取所有节点,判断自己不是最小节点,设置监听比自己小的节点;
(5)线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获
得锁。
(五)、分布式锁的优缺点
从理解的难易程度⻆度(从低到⾼)
数据库 > 缓存 > Zookeeper
从实现的复杂性⻆度(从低到⾼)
Zookeeper >= 缓存 > 数据库
从性能⻆度(从⾼到低)
缓存 > Zookeeper >= 数据库
从可靠性⻆度(从⾼到低)
Zookeeper > 缓存 > 数据库
四、秒杀5分钟取消订单方案设计
(一)、订单超时取消订单的五种实现方案
1、数据库轮询
方式:通过订单时间来判断是否有超时的订单。
优点:简单易行,支持集群操作
缺点:对服务器内存消耗大;存在延迟;数据库损耗极大
2、JDK的延迟队列
方式:利用 JDK 自带的 DelayQueue 来实现。
优点:效率高,任务触发时间延迟低。
缺点:服务器重启后,数据全部消失,怕宕机;集群扩展相当麻烦;容易出现 OOM 异常;代码复杂度较高
3、时间论算法
优点:效率高,任务触发时间延迟更低。
缺点:服务器重启后,数据全部消失,怕宕机;集群扩展相当麻烦;容易出现 OOM 异常
4、Redis缓存
方式:利用 redis 的 zset有序集合或者利用键空间机制。
优点:由于使用 Redis 作为消息通道,消息都存储在 Redis 中。如果发送程序或者任务处理程序挂了,重启之后,还有重新处理数据的可能性;做集群扩展相当方便;时间准确度高。
缺点:需要额外进行 redis 维护。
5、使用消息队列
方式:采用 RabbitMQ 的延时队列
优点:高效,可以利用 rabbitmq 的分布式特性轻易的进行横向扩展,消息支持持久化增加了可靠性
缺点:引用RabbitMq,复杂度和成本更高。
(二)、RabbitMQ的死信情况
1、死信队列
DLX(Dead-Letter-Exchange),当信息在一个队列变成死信(Dead message)后,能被重新发送到DLX中,绑定DLX的队列称之为死信队列。
2、成为死信队列原因
- 消息过期
- 消息被拒绝,requeue参数为false
- 队列达到最大长度