大型电商网站分布式秒杀系统设计三种姿势( 五 )


解决并发锁的问题,有两种办法:

  • 应用层排队 。通过缓存加入集群分布式锁,从而控制集群对数据库同一行记录进行操作的并发度,同时也能控制单个商品占用数据库连接的数量,防止热点商品占用过多的数据库连接 。
  • 数据层排队 。应用层排队是有损性能的,数据层排队是最为理想的 。业界中,阿里的数据库团队开发了针对 InnoDB 层上的补丁程序(patch),可以基于 DB 层对单行记录做并发排队,从而实现秒杀场景下的定制优化 。注意,排队和锁竞争是有区别的,如果熟悉 MySQL 的话,就会知道 InnoDB 内部的死锁检测,以及 MySQL Server 和 InnoDB 的切换都是比较消耗性能的 。另外阿里的数据库团队还做了很多其他方面的优化,如 COMMIT_ON_SUCCESS 和 ROLLBACK_ON_FAIL 的补丁程序,通过在 SQL 里加入提示(hint),实现事务不需要等待实时提交,而是在数据执行完最后一条 SQL 后,直接根据 TARGET_AFFECT_ROW 的结果进行提交或回滚,减少网络等待的时间(毫秒级) 。目前阿里已将包含这些补丁程序的 MySQL 开源:AliSQL
https://github.com/alibaba/AliSQL?spm=a2c4e.10696291.0.0.34ba19a415Ghm4
小结:高读和高写的两种处理方式大相径庭 。读请求的优化空间要大一些,而写请求的瓶颈一般都在存储层,优化思路的本质还是基于 CAP 理论做平衡 。
总结一下当然,减库存还有很多细节问题,例如预扣的库存超时后如何进行回补,再比如第三方支付如何保证减库存和付款时的状态一致性,这些也是很大的挑战 。
高可用
盯过秒杀流量监控的话,会发现它不是一条蜿蜒而起的曲线,而是一条挺拔的直线,这是因为秒杀请求高度集中于某一特定的时间点 。
这样一来就会造成一个特别高的零点峰值,而对资源的消耗也几乎是瞬时的 。所以秒杀系统的可用性保护是不可或缺的 。
流量削峰
对于秒杀的目标场景,最终能够抢到商品的人数是固定的,无论 100 人和 10000 人参加结果都是一样的,即有效请求额度是有限的 。并发度越高,无效请求也就越多 。
但秒杀作为一种商业营销手段,活动开始之前是希望有更多的人来刷页面,只是真正开始后,秒杀请求不是越多越好 。
因此系统可以设计一些规则,人为的延缓秒杀请求,甚至可以过滤掉一些无效请求 。
①答题
早期秒杀只是简单的点击秒杀按钮,后来才增加了答题 。为什么要增加答题呢?
主要是通过提升购买的复杂度,达到两个目的:
  • 防止作弊 。早期秒杀器比较猖獗,存在恶意买家或竞争对手使用秒杀器扫货的情况,商家没有达到营销的目的,所以增加答题来进行限制 。
  • 延缓请求 。零点流量的起效时间是毫秒级的,答题可以人为拉长峰值下单的时长,由之前的 <1s 延长到 <10s 。这个时间对于服务端非常重要,会大大减轻高峰期并发压力;另外,由于请求具有先后顺序,答题后置的请求到来时可能已经没有库存了,因此根本无法下单,此阶段落到数据层真正的写也就非常有限了 。
需要注意的是,答题除了做正确性验证,还需要对提交时间做验证,比如<1s 人为操作的可能性就很小,可以进一步防止机器答题的情况 。
答题目前已经使用的非常普遍了,本质是通过在入口层削减流量,从而让系统更好地支撑瞬时峰值 。
②排队
最为常见的削峰方案是使用消息队列,通过把同步的直接调用转换成异步的间接推送缓冲瞬时流量 。
除了消息队列,类似的排队方案还有很多,例如:
  • 线程池加锁等待
  • 本地内存蓄洪等待
  • 本地文件序列化写,再顺序读
排队方式的弊端也是显而易见的,主要有两点:
  • 请求积压 。流量高峰如果长时间持续,达到了队列的水位上限,队列同样会被压垮,这样虽然保护了下游系统,但是和请求直接丢弃也没多大区别 。
  • 用户体验 。异步推送的实时性和有序性自然是比不上同步调用的,由此可能出现请求先发后至的情况,影响部分敏感用户的购物体验 。
排队本质是在业务层将一步操作转变成两步操作,从而起到缓冲的作用,但鉴于此种方式的弊端,最终还是要基于业务量级和秒杀场景做出妥协和平衡 。
③过滤
过滤的核心结构在于分层,通过在不同层次过滤掉无效请求,达到数据读写的精准触发 。
常见的过滤主要有以下几层: