商品秒杀问题的思考

2020-03-20

商品秒杀场景比较常见,例如 12306 抢票等等。是高并发和分布式系统处理的经典案例。个人没做过秒杀项目,仅仅对这个问题感兴趣,研究了一下。个人认为秒杀场景主要有两个核心问题:到点抢购负载和商品超量。


1. 负载问题

秒杀活动开始,终端请求洪水般涌入服务器。请求包经过去重处理(uid/ip/other),放进消息队列,排队等待消费处理。

消息队列处理负载


2. 商品超量问题

主要思路:

  1. 独立服务维护商品数量。
  2. 商品数量服务提前批次扣量。
  3. 相同商品 id 落在同一个进程或线程处理。
  4. 数据库悲观锁。

有的朋友通过 redis 的 incr 命令处理超量问题,但是 redis failover 会导致数据丢失,这问题也相当棘手!

商品超量处理


详细思路:

  • logic server 消费处理抢购请求。
  • num server 维护商品数量。
  • logic server 在处理抢购请求前先扣量,抢购请求处理失败或提交订单操作超时补量。
  • logic server 扣量补量与 num server 交互。
  • logic server 请求根据商品 id 为因子,通过一致性哈希,将同一个商品的量操作发送到相同的 num server 进行处理。
  • num server 商品数量落地 MySQL,用悲观锁,主库上操作保证数据一致性。
  • num server 为减少数据库的读写频率,从 MySQL 扣除一批次的量,放进进程内存。批次数据要进行记录,每次处理完毕,再进行下一批次数据的申请。批次处理类似于代理和厂家关系,代理卖完一批商品,再从厂家进货。
  • num server 定时监控商品数据量变化,如果长时间不访问,内存中没分派完的量要回写 MySQL。
  • num server 异常退出,重启或新节点启动需要检查上一批次是否完成,如果没完成需要统计该批次的量分发情况,把没有分发的量统计回收(每个分派出去的量,都通过 kafka 记录流水)。

3. 小结

  • 系统将复杂的商品量维护逻辑都交给 num server 了,减少了业务复杂度的耦合。其它业务服务只需通过简单接口进行扣量和补量即可。
  • num server 批次扣量操作,减少了业务对数据库的读写频率,提高了业务逻辑的并发。
  • num server 每个分派的量,都需要通过 kafka 记录流水,方便异常统计,也相当于数据埋点。
  • 这个秒杀处理方案,在分布式集群中添加了 ketama 一致性哈希算法,logic server 将同一个商品量的维护集中到一个 num server 节点中处理,避免了多节点同时处理一个商品 id,需要增加全局锁。当然这增加了一些复杂度,num server 当单节点 failover 处理过程中,要处理好上一次处理的状态。
  • num server 只是一个商品量的维护,同理,也可以通用到其它的一些 id 分配。

4. 参考