秒杀场景

整体要求

  1. 保证系统的高可用和正确性
  2. 一致性:保证秒杀减库存中的数据一致性。
  3. 高性能:涉及大量并发读写,需要支持高并发,从动静分离、热点发现与隔离、请求削峰与分层过滤、服务端极致优化来介绍。

数据预热(预加载)

  1. 将秒杀商品提前加入到缓存系统入ES、Redis等,防止商品超卖和缓存穿透甚至雪崩。

限制

  1. 通过网络代理层、SLB负载均衡层、程序阻流组件与算法(如Guava限流)、前端逻辑过滤等多种手段,防止大流量而造成服务拒绝或阻塞。

削峰

  1. 通过异步通信的设计与解决方案如RPC、MQ等具体实现

隔离

  1. 通过部署隔离方案,在网络拓扑将秒杀系统独立部署、即使异常或宕机,至少不会导致主营业务系统受损或瘫痪,库表分区。

业务兜底方案

一般这种,宁可少卖不可多卖,扣减库存的方式:

  1. 下单减库存
    实现:下单操作绑定减库存操作
    优点:一定不会出现超卖
  2. 付款减库存
    实现:付款时,再扣减库存
    优点:并发高,买家下完单却付不了款
  3. 预扣库存
    实现:下单后,库存保留一段时间,超过时间自动释放,释放后其他买家就可以自己购买了,买家付款前,会校验该订单的库存是否还有保留,如有保留再次尝试预扣。如果预扣失败,则返回失败,预扣成功则付款成功并实际减少库存。
    redis 实现:lua脚本实现 检查库存+扣减库存+返回结果
    mysql 事务实现:
    update 新数量=原数量-购买数量 where 原数量>购买数量 and 商品id = xxx

库存放入缓存中的方法

  1. 多个redis 分别存储部分商品库存。用户请求随机到其中一个redis上,能获取到就获取,不能就返回失败(哪怕另外几个redis中库存存在)

分布式锁场景

分布式锁的一般要求

  1. 分布式环境下的一致性
  2. 可重入锁
  3. 尽量阻塞锁
  4. 尽量公平锁
  5. 高可用
  6. 快,性能好

redis中遇到过的问题总结

  1. 主从复制:主从复制的异步复制,从尚未同步完主数据,主挂了,发生主从切换会导致数据的一致性问题。
  2. setnx 成功 expire 设置失败时,会出现key无限存在的问题。使用redis实现分布式锁中,那么会造成死锁。
    解决方案1:set 命令 set key value [EX seconds] [PX milliseconds] [NX|XX}
    EX seconds:失效时长 (s)
    PX milliseconds:失效时长 (ms)
    NX:key不存在时设置value,成功返回OK,失败返回nul
    XX:key存在时设置value,成功返回OK,失败返回nil
    解决方案2:lua脚本实现setnx 与 expire 原子性
  3. key的续约机制
    业务线程需要定期刷新key的有效期,直到使用完毕key过期