以下是王平老师分享全文(经 InfoQ 进行不改变原意的编辑整理):
如上图,当我们回顾参与过的秒杀和热门活动,如双 11 大促、12306 节假日抢票、疫情期间抢菜以及门票 / 演唱会抢票时,大家有相似的感受:
a) 定时开售:活动总定时开售,所有人都在同一时刻涌入。
b) 系统压力:在抢购那一刻,系统常常出现卡顿、宕机或提示“太火爆”或需要排队等待,让人倍感焦虑。
c) 结果未知:尽管全力以赴,但结果往往不尽如人意,有时抢到了也可能被退单。
这些活动在系统中也呈现出相似的特征:
a) 大流量高并发:大流量、高并发,对系统性能提出了严峻挑战。
b) 时间敏感性:准时开售,用户争抢热点资源,系统需要确保准时、准确地响应。
c) 履约保障:从预前到预后,系统都需要确保履约的顺利进行,避免用户因系统问题而产生不好的体验和焦虑。
与普通电商相比,门票交易系统具有两大特点:
强一致性: 即用户预订后必须确保出票,确保每一笔交易都能履约。不能接受用户付款后无票的场景。
多维度和跨商品组合限购: 限购规则复杂多变,包括多维度和跨商品组合限购,旨在保障每位用户公平购票的机会,避免囤票行为 。
这些特点不仅体现了系统的专业性,也对技术能力提出了更高要求。
接下来我们回顾一下历史大型秒杀 / 活动案例。
过去几年经历过的一些大型营销活动,一个比较好的系统架构肯定是经过一系列的业务后慢慢迭代出来的。携手门票里比较重要的三个环节,从 2020 年的湖北 0 元票,当 2024 年 4 月份的一个 IU 的一个演唱会,然后其实它的一个峰值的 QPS 其实达到了数百万了。
案例回顾
a) 2020 年 8 月 8 日~9 月 1 日:“惠游湖北”活动,携程独家承办,面对日常流量 45 倍 (数十万 QPS) 峰值的流量挑战,虽然刚开始遇到各种挑战,系统出现不稳定的情况,但最终还是成功应对。
b) 2021 年 9 月 14 日:北京环球影城开业,携程在与其他友商的竞争中,表现最为稳定且销量最高。
d) 2024 年 4 月 10 日:IU(李知恩)全球演唱会门票售卖,携程再次展现其强大的系统能力,预订过程丝滑流畅,10 秒内售罄。
秒杀活动以其短暂的时间窗口和高度的用户参与度,对系统的承载能力构成了巨大的挑战。
以下是部分历史秒杀活动峰值流量与日常峰值流量的对比数据:
通过对比历史大型秒杀活动的峰值流量与日常峰值流量,不难发现这些活动所带来的流量激增远超系统日常处理的极限。在短时间内处理巨大的流量,不仅要求系统具备高度的可扩展性和灵活性,还需要在架构、缓存、数据库、流量控制等多个方面进行全面优化。
大流量活动常见问题归纳
总结了用户的反馈和系统的问题,主要有以下 2 类问题:
1. 页面打开慢、卡顿、宕机:直接影响用户购物体验。系统表象:Redis、DB 超负载,供应商不稳定等。
2. 付款后不能确认 / 退款:付款后,无法及时确认订单状态或进行退款操作。系统表象:系统库存出现超卖 / 少卖等。
针对上述问题,我们需要对交易系统架构进行迭代优化。并且以“稳、准、快”为设计目标,
稳:确保系统稳定可靠,保障售卖流程无间断。
准:实现数据一致性,确保履约准确无误。
快:提供流畅的预订体验,实现快速确认。
在大流量高并发场景下,要达到这些目标还需要做更多的努力。 接下来分别说一下,我们如何达成这个目标。
在面临系统稳定性受到严峻挑战时,主要遭遇了页面打开慢、卡顿等问题。经过深入分析,主要原因有以下几点:
Redis 超负载与缓存热点。
数据库超负载。
供应商系统不稳定。
接下来,我们探讨一下这 3 类问题的常用解决方案。
问题一:Redis 超负载与缓存热点
针对 Redis 比较常规的方案是进行水平扩容,让流量能够分摊到更多的实例。该方案确实能够降低整体的 CPU 的 水位,但在处理特定热点数据时,如热门商品秒杀等场景,各实例的 CPU 使用率仍然可能出现不均衡的情况,这就是所谓的缓存热点问题。
一、 缓存热点问题
如下图所示,node-1 节点存在 2 个热点访问,请求量远高于其他节点。缓存热点会导致实例负载不均衡,从而拖垮响应速度。
缓存热 Key 应对方案:热点识别自动构建多级缓存
将单位时间内高频访问的 Key,识别出来。例如:同一个 Key,1 秒内单机访问 10 次。
如上图所示,自动发现 Hot keys 或将指定的 Key 加入到本地缓存。
秒杀时:短暂的本地缓存可以减少 Redis 单实例热点,对数据的一致性不会有较大影响。
效果: 开启多级缓存后,同一个缓存 key 访问性能明显提升,对应 Redis 访问量也明显降低(如下图所示)。
二、 缓存大 Key 问题
缓存大 key 的危害主要包括:阻塞请求、内存占用大、阻塞网络等。不仅会降低 Redis 的性能,还可能影响整个系统的稳定性 (如下图 - 左)。通过 memtier-benchmark 工具在生产环境下压测,结论如下 (如下图 - 右):
200KB 以上比 10KB 以内的性能慢 3 倍,吞吐能力也下降 76%(如下图所示)
缓存大 Key 应对方案:
a) 精简缓存对象: 去除缓存中的冗余字段。
b) 压缩缓存对象: 采用压缩比更高的压缩方式,缩小缓存对象。
c) 拆分大 Key: 若精简和压缩后还是过大,根据业务逻辑,将大 Key 拆分成多个小 Key。(注意拆分后 IO 次数会增加,高负载下性能不一定会变好,需要根据压测结果来评估最终性能)
长期治理: 建立长期治理机制,定期扫描 Redis 中的大 Key,体现到研发系统质量指标上,由应用 owner 跟进,将隐患在日常治理中消除。
效果: 效果大 Key 优化后,Redis 查询性能有较为明显的提升(如下图所示,缓存查询耗时从 300μs 优化至 100μs)
问题二:数据库超负载
系统中商品信息的变更往往伴随着缓存失效的问题,尤其在高并发和秒杀场景下,大量请求可能直接穿透缓存层,对数据库造成巨大压力,甚引发性能故障。
缓存更新策略优化:应对商品变更导致的数据库压力
1. 常见的缓存架构设计问题
通过监听缓存数据变更消息来删除相应的缓存 Key。这种方式在一般情况下是有效的,但在高并发和大流量场景下,它存在几个显著的问题:
a) 缓存击穿:由于缓存的 Key 被立即删除,大量请求在缓存未更新之前会直接访问数据库,导致数据库压力骤增。
b) 消息处理延迟:在高并发场景下,消息处理可能产生延迟,导致缓存更新不及时,进一步加剧数据库压力。
2. 缓存更新策略的优化
为了应对这些挑战,我们采取了一系列优化措施,主要包括:
a) 缓存覆盖更新策略:替代直接删除缓存 Key 的做法,我们采用了缓存覆盖更新策略。当商品信息发生变更时,系统不再删除缓存 Key,而是直接更新该 Key 对应的缓存值。避免了流量穿透到底层数据库。
b) 消息聚合: 针对商品变化消息量过大的问题,我们引入了消息聚合机制。将商品多次变化消息在一段时间窗口内合并成一个,减少消息处理的频率。
c) 异步更新缓存: 为了进一步降低对数据库的实时压力,我们采用了异步更新缓存的策略。当商品信息发生变更时,系统不会立即更新缓存,而是将更新任务放入一个异步队列中,由后台线程异步处理。
问题三:供应商系统不稳定
不同供应商系统负载能力不一样,往往因大流量冲突导致响应缓慢或被限流,影响整体系统的稳定性。
应对供应商系统不稳定性的技术策略优化
当供应商系统面临大流量冲击时,往往会出现响应缓慢甚至被限流的情况,这直接影响了我们自身系统的稳定性和用户体验。
供应商订单对接问题
当与供应商进行订单对接时,我们可能会遇到以下问题:
a) 被供应商限流:在高并发场景下,供应商系统可能会对我们实施限流措施。这会导致我们的订单提交受阻,影响业务流转。
b) 供应商系统不稳定:由于各种原因,供应商系统可能会出现不稳定的情况,导致订单处理延迟或失败。
为了缓解上述问题,我们采取以下技术策略:
a) 削峰填谷 **/** 缓冲池:利用消息队列作为订单提交的缓冲池,将订单信息先写入队列,再由后台服务异步处理。这样可以将订单提交的高峰流量削平,减少对供应商系统的瞬时压力。
b) 禁售策略
自动禁售:建立对供应商系统的健康度监控机制,实时监测其响应速度、错误率等指标。一旦发现供应商系统出现不稳定或限流的情况,及时触发禁售策略。
定期重试:对于因供应商系统问题而失败的订单,设定了一个重试机制,定期尝试重新提交。同时,根据供应商系统的恢复情况,动态调整重试的频率和次数。
效果: 通过实施上述技术和策略优化,可以有效确保供应商系统能力不影响下单(收单)吞吐量 (如下图所示)。
大流量的冲击往往是对系统稳定性的重大考验,上述的优化措施均旨在增强系统能力。鉴于流量具有不确定性,如何在保证用户体验的同时,减少不必要的资源投入,即使流量超过系统负载能力, 系统也要正常运行,也是我们要解决的问题。
流量控制策略优化:确保营销活动稳定运行
如下图所示,不同页面对应的流量和系统 (承载能力) 是不一样的,我们需要控制好每一步的流量,确保整体系统的稳定性。
以 30 万人购买 5000 张票的秒杀活动为例,我们采取以下限流措施来保障各系统的稳定运行:
1. SOA 限流与自定义限流结合
通过服务治理框架对服务接口进行限流(SOA 限流),在秒杀 / 活动等场景会影响到其他商品的正常售卖。对此,我们针对秒杀活动的特殊需求,设计自定义的限流策略,如按秒杀商品限流、页面级限流等,细化了商品维度的流量控制。
1. 自定义限流:商品级限流
a) 针对每个秒杀商品设置独立的限流阈值,确保即使某个商品超负载,也不会影响整体系统的可用性。
b) 同时,如果对于未知的秒杀突增流量,我们也支持热点商品自动限流,与 Redis 热 Key 发现一样,自动识别热点访问的商品,并添加到商品级限流中,确保整体系统稳定运行。
如下图所示,我们采用了商品维度的自定义限流策略,该策略将 1 秒内的请求流量划分为 10 个独立的 100 毫秒 (可配置) 滑动窗口。每个窗口都会平分一部分流量,以确保下游服务的并发量得到有效控制。这种方法不仅降低了下游服务的压力,也为用户提供更加均衡的流量分配。
结合商品级限流能力,我们控制进入每一个页面的流量,形成多层次的限流防护体系,根据秒杀库存预估售卖时长,控制进入到每一个页面的流量比例,这样也减少了大量的服务器资源的投入。
效果: 自定义限流可控制进入每一个页面的流量,超负载也不影响整体的可用性,服务器资源的投入也可控。
第一部分主要讲述关于稳定性的挑战及优化,包括 Redis 超负载与缓存热点、数据库超负载、供应商系统不稳定等。通过多种技术策略,如热点识别自动构建多级缓存、缓存覆盖更新策略、削峰填谷 / 缓冲池、自定义限流等得以有效的解决。
下单过程中的库存扣减的精确执行,直接影响到是否能够成功履约。
扣减库存问题 :性能瓶颈 – MySQL 热点行扣减库存(行级锁)
技术策略: 扣减库存异步化,异步扣库存主要分 3 步 (如下图):
初始化:秒杀商品设置好活动场次,将秒杀库存同步至 Redis。
扣库存:活动开始时,先从 Redis 扣减库存,在通过消息通知异步扣减 DB 库存,削除 DB 更新同一行的峰值。
还库存:如果有退订,先判断 DB 中是否有扣减记录,如果有,则先退 DB 再退 Redis;如果没有,重试多次。
当然扣还库存过程中存在超时等未知情况,这里面还有许多细节不再展开。业务上按“可少买不超卖”的原则,即使在这个过程中数据可能存在短暂的延时,但会通过一系列机制确保最终的一致性。
优化效果: 库存扣减异步化,消除行级锁瓶颈。现可轻松支撑数十万单 / 分钟交易流量。
这成果来之不易,我们该如何将这些宝贵的成果稳固地保留下来,并推动其长期、稳定的发展呢?
让系统在大流量冲突下能稳定运行只是跨出了第一步,如何让这些优化成果能够在未来保持稳定呢?
日常架构健康度持续治理与大型活动节假日保障体系是我们高可用“可持续性”的关键。
架构健康度治理:系统架构健康度治理,按照研发生命周期顺序划分,如下图所示:
系统设计健康度:服务的数量会导致应用间关系变的复杂,循环依赖,调用层级过深等都影响着系统的稳定性和性能。
工程文化健康度:系统工程化健康度基于应用的工程质量和效率状态,反应出开发的工程化水平。例如:编译时长,启动时间等。
系统运行健康度:接口性能,DB 高频和慢查询、Redis 大 Key,热 Key 等反应出系统运行时的健康状态。
2. 大型活动节假日保障体系:在大流量冲击下,有较高的风险,大型活动与节假日一样,需要提前准备好应急预案,确保系统的高可用。
总结
在大流量高并发下,系统的挑战与日常有较大的差异。日常影响系统健康的每一个细节都至关重要,小问题在这样的环境下会被放大。
以上总结了携程门票交易系统在承接秒杀 / 营销活动中的挑战与应对策略。我们重点解决了不同介质的读热点、写瓶颈、强事务,流量控制等诸多细节问题,同时制定专项的保障计划和日常的架构健康度治理,持续对系统进行优化,以确保系统在高负载下依然能够稳定运行,保障系统的持续高可用。
AICon 全球人工智能开发与应用大会,为资深工程师、产品经理、数据分析师等专业人群搭建深度交流平台。汇聚 AI 和大模型超全落地场景与最佳实践,期望帮助与会者在大模型时代把握先机,实现技术与业务的双重飞跃。在主题演讲环节,我们已经邀请到了「蔚来创始人 李斌」,分享基于蔚来汽车 10 年来创新创业过程中的思考和实践,聚焦 SmartEV 和 AI 结合的关键问题和解决之道。大会火热报名中,7 月 31 日前可以享受 9 折优惠,单张门票节省 480 元(原价 4800 元),详情可联系票务经理 13269078023 咨询。