秒杀系统分析

118 阅读6分钟

秒杀业务分析:

一、什么是秒杀

就是商家发布一些活动,使得商品降价,用户在同一时间内大量抢购商品。例如双十一、双十二购物节。

秒杀场景特点:

(一)、秒杀时大量用户会在同一时间同时进行抢购,网站瞬时访问流量激增。

(二)、秒杀一般是访问请求数量远远大于库存数量,只有少部分用户能够秒杀成功。

(三)、秒杀业务流程简单,下单,减少库存。

可能遇到的技术点:

(一)、限流

(二)、削峰

(三)、异步处理

(四)、超卖问题

(五)、分布式锁

二、秒杀方案设计

(一)、核心思想:

运用缓存,减少数据库访问的瞬间压力。

(二)、设计图

image.png

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. 在分布式系统环境下,⼀个⽅法在同⼀时间只能被⼀个机器的⼀个线程执行;
  1. 高可用的获取锁与释放锁;
  1. 高性能的获取锁与释放锁;
  1. 具备可重入特性;
  1. 具备锁失效机制,防止死锁;
  1. 具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。

(四)、分布式锁实现的几种方式

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
  • 队列达到最大长度