秒杀系统场景设计

秒杀系统通常需要考虑:高性能、高可用、一致性、限流

高性能

一个秒杀活动,不可避免的涉及到高并发。在高并发场景下,通常将数据放入redis缓存(设置过期时间),然后再放入JVM内存(减少网络开销)。对于一些静态资源比如图片之类的,通过CDN (内容分发网络)处理,将图片之类的分发到不同地方以实现就近访问。

一般来说,如果是商品秒杀或者演唱会门票秒杀这类,我们明知特定商品和接口会迎来高并发,就需要提前进行缓存预热,将商品或者门票提前写入缓存中。

这类数据称为热点数据

热点数据分为静态和动态

静态就是上述的门票或者商品。

动态可以理解为某个商品突然爆火,我们不可预知的,需要上手段才能检测出来的。

动态检测有哪些手段:

中间件:例如京东的hotkey。

或者通过redis快速定位热key:比如

1 通过代理,所有的redis请求都通过代理,改动 Proxy 代码收集信息,可以解决多语言问题,但是开发难度大

2 通过reids定时扫描,在redis4.0之后,可以通过 redis-cli --hotkeys 获取当前的热key。实际上是通过scan+object freq完成。但由于要扫描所有key,耗时大,效果不好

3 reids节点抓包,在可能产生热key 的节点上进行抓包并解析计算。不过会造成给节点增加负载

高可用

一般而言都是集群。例如redis集群,使用Sentinel哨兵+一主多从结构。

一致性

一般就是两个一致性,库存数量和扣款金额

库存一般采用下单就扣库存,例如抢票的时候,会有10分钟支付时间,虽然还没支付,但是库存已经扣掉,只有超时没付钱或者关闭付费才释放库存。

通过redis+LUA脚本实现。通常数据一致性是通过更新数据库,再删除缓存来保证一致性。

在这里确定redis扣减库存后,可以通过先对redis的库存进行更新,然后通过MQ保证消息被消费了就行。这样确保数据库最终和缓存是一致就行。

对于金额就要慎重:

金额需要强一致性。并发量高的时候使用悲观锁,低的话使用乐观锁(版本号)。数据库还要确保唯一索引。

悲观锁一般通过redis或zookeeper实现分布式锁。也可以通过mysql的排他锁(innodb,事务)

redis一般是通过redisson来实现的,在redis深入中有所介绍

zookeeper的分布式锁基于 临时顺序节点 和 Watcher(事件监听器) 实现的。

获得锁

  1. 首先我们要有一个持久节点/locks,客户端获取锁就是在locks下创建临时顺序节点。
  2. 假设客户端 1 创建了/locks/lock1节点,创建成功之后,会判断 lock1是否是 /locks 下最小的子节点。
  3. 如果 lock1是最小的子节点,则获取锁成功。否则,获取锁失败。
  4. 如果获取锁失败,则说明有其他的客户端已经成功获取锁。客户端 1 并不会不停地循环去尝试加锁,而是在前一个节点比如/locks/lock0上注册一个事件监听器。这个监听器的作用是当前一个节点释放锁之后通知客户端 1(避免无效自旋),这样客户端 1 就加锁成功了。

释放锁:

  1. 成功获取锁的客户端在执行完业务流程之后,会将对应的子节点删除。
  2. 成功获取锁的客户端在出现故障之后,对应的子节点由于是临时顺序节点,也会被自动删除,避免了锁无法被释放。
  3. 我们前面说的事件监听器其实监听的就是这个子节点删除事件,子节点删除就意味着锁被释放。

幂等

幂等问题是确保消息只被消费一次。

实际项目中通过分布式锁来保证:redis、zookeeper。(在短链接项目中有用到)

或者通过token机制

具体流程步骤:

1.    客户端会先发送一个请求去获取 token,服务端会生成一个全局唯一的 ID 作为 token 保存在 redis 中,同时把这个 ID 返回给客户端。

2.    客户端第二次调用业务请求的时候必须携带这个 token。

3.    服务端会校验这个 token,如果校验成功,则执行业务,并删除 redis 中的 token,然后再执行业务。

4.    如果校验失败,说明 redis 中已经没有对应的 token,则表示重复操作,直接返回指定的结果给客户端

限流

限流一般是对接口限流:redis+LUA脚本 和 Sentinel(提供了流量控制、熔断降级等) 两个方法都可以实现。

有的时候也会通过限制用户和IP,比如限制他每秒访问次数,防止脚本抢单

削峰:通过MQ实现,当用户发送秒杀请求后,放入到MQ中,慢慢消费。(如果接口限流了其实不需要削峰)

降级:保证核心业务,如果服务器有压力,且某些页面有降级策略,那么那些页面就会释放服务器资源保证核心业务的进行

熔断:例如电闸的保险丝,如果电流过大,就会使得保险丝断裂,保护整个电箱不受破坏。在分布式系统中,某些服务依赖下游服务,如果下游有问题,例如秒杀在服务A上,服务A上还有其他功能,比如管理某商品之类,此时管理某商品的请求访问时间就会非常长,长时间的等待增加了链路的请求时间,如果后续不断有管理某商品的请求就会导致服务器COU拉满之类的问题,所以就会触发熔断,直接不让大家访问管理某商品的这个服务。

  • 17
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值