架构 (IT)系统架构师

打造一个全球分布的事件源库存管理骨架,该骨架在闪电销售流量洪水期间协调异构仓库管理系统之间的实时库存分配,保证严格的可串行性以防止超卖而不出现分布式锁瓶颈,并通过补偿事务模式与遗留ERP集成调和最终一致性漂移。

用 Hintsage AI 助手通过面试

对问题的回答

历史:传统的电子商务平台依赖于单体RDBMS实例,它们在每秒超过100,000个并发结账的闪电销售负载下崩溃。行业向CQRS事件源模式转变,以解耦读写路径,但这在维护异构WMS和遗留ERP孤岛之间的库存准确性时引入了复杂性,并且它们具有不同的延迟特征。

问题:核心挑战在于在网络分区期间满足CAP定理约束,同时防止超卖——这是一个严格的业务不变式。分布式锁机制如RedLock引入了延迟和可用性风险,而纯粹的最终一致模型则有超卖虚假库存的风险。此外,与基于遗留SOAP/XMLWMS系统的异构集成点造成了阻抗失配和超时级联,复杂化了原子事务边界。

解决方案:实施一个事件存储(例如Apache KafkaEventStoreDB)作为库存增量的真实来源,使用乐观并发控制向量时钟来建立因果排序,而无需全球锁。采用Saga协调(使用TemporalCamunda)管理跨WMS交易,其中本地预留立即提交到事件存储,并且来自WMS的异步确认触发最终的分配或补偿释放。为了实现读取可扩展性,通过DebeziumCDC的方式部署CQRS,投影到RedisElasticsearch,确保读延迟低于50毫秒,同时接受因预留TTL造成的临时陈旧。

生活中的情况

在2022年黑色星期五准备期间,一家全球时尚零售商在50,000名并发用户针对限量版运动鞋发布时遭遇了灾难性的数据库超时。其现有的MySQL主从拓扑在热门库存行上遭遇严重的写入竞争,导致结账延迟达到12秒,并因主副本之间的复制延迟而造成300起确认超卖事件。业务需要一种解决方案,能够在保持严格的不变式(可售单位从不超过实际仓库库存)的同时,吸收闪电销售流量洪水。

工程团队最初建议在三个可用性区域中实施Redis RedLock算法,以在库存减少期间强制进行分布式互斥。这种方法的优点是提供了团队熟悉的强一致性保证,并且与现有的用于会话管理的Redis集群的集成非常简单。然而,关键缺点包括在可用性区域故障期间不可接受的延迟尖峰,超过500毫秒,并且理论上时钟偏差可能会使锁的安全属性失效,这在最关键的创收窗口期间可能导致库存分配死锁。

另一种策略包括通过SKU范围水平切分数据库,并采用两阶段提交协议以在区域PostgreSQL实例之间维护ACID保证。这种解决方案提供了强一致性和即时库存精确度的好处,没有复杂的最终一致性模式,契合了传统事务思维模式。然而,缺点证明是不可接受的:2PC的阻塞特性意味着协调者故障可能会无限期地保持数据库锁,而该协议的消息复杂性在流量高峰期间造成了网络饱和,根本违反了24/7全球商业所需的可用性要求。

最终候选架构采用了事件源Apache KafkaSaga协调,接受BASE语义,同时通过补偿事务强制业务不变式。优点包括通过可分区的事件流提供固有的水平可扩展性,以及为欺诈分析提供的不可变审计跟踪,并通过幂等事件消费者与异构遗留WMS自然集成。主要缺点涉及对于不熟悉不可变数据模式的开发者来说陡峭的学习曲线,以及管理事件模式演变和新读取模型投影的操作复杂性。

架构委员会选择了事件源方法,因为闪电销售根本上优先考虑可用性和分区容忍度,而不是立即一致性,业务逻辑能够容纳五分钟的临时软预留(TTL),而不是硬数据库锁。与锁定替代方案不同,这种设计允许系统在数据中心之间的网络分区期间保持可用性,确保客户始终可以尝试购买,即使仓库确认经历延迟。此外,不可变事件日志提供了财务团队所需的可审计性,以与第三方物流提供商调和差异。

该实施方案部署了Kafka Streams用于本地库存聚合管理,Temporal用于跨SAP和自定义WMS系统的Saga协调,以及使用写入缓存的Redis进行查询优化。在随后的网络星期一活动期间,该平台成功处理了120,000个并发结账,p99延迟低于80毫秒,并且未发生超卖事件,尽管在us-east-1的模拟区域故障中保持了99.99%的可用性,这将使之前的单体架构瘫痪。

候选人常常忽视的事项

如何在不使用全局锁的情况下处理跨多个Kafka分区的并发预留以防止虚假库存?

虚假库存发生在并发命令从不同分区读取过时的库存水平时,并且两者都提交了超过实际可用性的预留。为防止这种情况发生而不使用全局锁,请在事件存储中实现使用版本号的乐观并发控制;每个库存聚合维护单调计数器,并且命令包含预期版本,存储在版本不匹配时拒绝追加,这迫使客户端重试。此外,通过将SKU散列到特定分区,确保分区亲和性,保持每个聚合的单写语义,从而完全消除跨分区协调。

当遗留WMS SOAP端点在本地事件存储已经提交库存减少后超时时,补偿事务策略是什么?

此场景代表了部分故障,其中本地状态与外部现实发散,需要使用Saga模式进行向后恢复。当WMS适配器遇到超时时,它向Saga协调器发布超时事件,然后附入一个库存_释放事件将库存返回到可用池,同时保持幂等性键以防止重试时重复分配。关键是,从不删除原始的减少事件;而是附加补偿事件以保持事务尝试的完整时间历史和审计轨迹。

在重建过程中,如何在不向客户暴露不一致库存数量的情况下处理事件重放和读取模型重建?

重放事件以重建CQRS读取模型,如果在查询执行时投影增量更新,可能会呈现临时错误库存水平。解决方案使用蓝绿部署来处理读取模型:创建一个从零偏移消费事件日志的影子投影,而现有实例则提供流量,然后在影子追赶时原子切换路由。此外,利用Kafka日志压缩和定期S3快照来减少重放时间,确保新的投影在重建过程中将潜在不一致的窗口降到最低。