库存合并扣减:一种基于分布式缓存的强一致性热点库存扣减方案

简介: 本文介绍了一种基于Redis分桶扣减与DB合并提交的强一致库存扣减方案,适用于热点商品高并发抢购场景。通过Redis实现高性能扣减计数,结合数据库明细保障数据准确,既避免超卖少卖,又显著提升TPS与系统稳定性,有效支撑直播等大流量业务需求。

前言

本文主要介绍了针对热点深库存下单抢购场景,库存团队设计并实现的一种基于Redis分桶扣减计数和合并提交扣减DB的方案;该方案基于分布式缓存实现,但也能做到不超卖不少卖,在保证扣减强一致性的同时,也提升了库存热点扣减TPS和扣减稳定性。


背景介绍

自库存团队成立以来,随着业务的不断发展,库存扣减性能优化一直是库存团队不断面临的挑战;近几年,库存和数据库团队一起,从数据库内核优化、到应用层优化、再到扣减架构升级,持续的提升库存DB扣减的性能。在前述优化的基础上,库存数据库的整体性能得到了很好的提升。

然而随着直播业务的发展,热点扣减的场景越来越频繁,对于深库存热点扣减的场景,日常我们只能通过限流的方式来保护DB,库存合并扣减就是在这样的背景下提出的,我们希望继续优化热点扣减的性能,使库存扣减不成为热点下单链路的瓶颈所在。


方案调研

传统的提高热点扣减的方案都是通过分布式缓存实现,该类方案通过将库存从 MySQL 划分到多个Redis分桶,扣减的时候直接走Redis进行扣减,分析此类方案,我们会发现:

1. 该类基于缓存的扣减方案确实能够大幅提升扣减的性能;

2. 但是这个库存扣减方案在实际应用中也会存在一些问题:

  • 无法避免库存少卖:针对Redis失败(如超时)的场景,应用层实际并不知道Redis库存是操作成功还是操作失败。为了避免库存超卖,对于扣减超时的场景,应用层只能默认当做失败处理;对于库存回补的场景,应用层只能默认当做成功处理。此时整体是业务有损的,所以Redis分桶扣减方案一般是用在卖家限购、权益这类非实物类库存扣减的场景,这类场景业务接受少卖
  • 对于稍微复杂的库存扣减模型无法支持:Redis库存扣减方案中使用incr和decr实现库存的加减,利用lowBound防止库存减到负数,value即是剩余的库存数量;这个方案只支持库存记录仅包含一个数值的库存模式, 对于sq- 同时wq+ 这种模式无法支持。
  • 库存扣减完全依赖Redis:当前扣减方案需要强保障Redis的稳定性,如果Redis异常则整个扣减链路异常。

我们想利用Redis提升扣减性能,但是我们的库存扣减模型相对更加复杂、同时我们的业务场景对于库存数据准确性要求更高,绝对不允许超卖少卖;传统的基于redis分桶扣减的方案并不完全适合我们的业务场景。


合并扣减方案

介绍合并扣减详细方案之前,先简单说明下库存域的几个基本概念,方便大家更好理解后续的内容。

1. 库存核心字段:

  • 可售库存:sq,实际可售的库存数量;
  • 预扣库存:wq,用户下单后,库存sq转向wq;
  • 占用库存:oq,货品仓库存模式,付款之后,库存从wq转向oq。

2. 库存扣减明细(也叫单据):是库存扣减的快照,明细是不可或缺的。明细的作用:

  • 记录扣减信息:下单是交易会告诉我们扣多少库存,后续付款或者订单取消时,上游不用关心库存的回补数量,库存内部从明细恢复;
  • 用于分布式系统交互过程中的幂等实现:同一个单据调了两次扣减;单据回补超时,重试可幂等;
  • 负责库存扣减生命周期状态流转。

整体思路

我们既想利用分布式缓存(Redis)提升扣减性能,但同时又不能直接依赖Redis作为库存存储介质;因此我们的方案里面,Redis只用来做扣减计数以防止超卖,实际扣减是否成功以库存扣减明细为准。 整体思路如下:

1. DB行的库存提前预留(锁)一部分,锁的这个库存还在DB行上,同时也初始化到redis里面用于扣减计数;

2. 合并扣减下单过程中,先扣减redis库存,redis库存够扣的情况下,再往DB插入扣减明细,明细插入成功最终才算扣减成功;

3. 应用层的合并扣减管理模块,热点下单延迟1s后,通过扫描扣减明细,计算实际应该扣减的数量,一次提交库存变更到DB。

接下来介绍各个分模块的设计。

锁库存模块

1. 锁库存模块将DB行库存从sq字段(可售)锁到lq字段(预锁库存),同时锁库存单据;

2. 锁库存核心实现,不是通过将sq减去然后加到lq,而是sq不动,锁库存直接加到lq;DB扣减时通过 where sq-lq>0 控制最终sq的数量不能小于lq。这个设计使得锁库存之后,库存展示链路完全不受影响,查询可售库存时也完全不需要关心锁的库存值,大大的降低整个链路上的复杂性,这是一个非常重要的设计。

3. 每次锁定的lq数量,初始化到Redis里进行扣减计数。

多个redis分桶通过redis分桶缓存进行索引:

下单扣减模块

先扣redis分桶库存,成功则继续插入DB明细;失败则兼容走老的DB扣减流程:

合并提交模块

1. 先失效合并提交对应的redis分桶,防止后续继续有交易流量扣减此redis分桶;

2. 根据分桶key扫描此分桶关联的所有扣减明细,计算库存扣减数量,合并扣减DB。

  • 实际Redis分桶扣减的库存数量,以合并下单明细为准,通过扫描明细上的扣减库存总和得到;
  • 这里我们针对数据库明细表,设计了覆盖索引(invId,lockOrderId,quantity),通过sum(quantity) 走索引就能直接得到每个Redis分桶实际扣减的库存;压测和线上效果都表明,此扫描单据的SQL对DB热点扣减的性能影响非常小。

库存回收模块

1. 库存回收指将预锁定的库存释放回收到可售卖的状态,以下几种场景需要回收库存:

  • 商家编辑场景,直接走的DB扣减,redis有剩余库存,而商家编辑时将总库存编辑成0,此时我们需要先释放预锁库存;
  • 临界场景:Redis分桶只有1件库存,DB两件库存,下单两件库存库存,此时将Redis的库存回收到DB后继续扣减。

2. 库存回收在实现上可以直接通过调用分桶合并提交来完成。

防超卖少卖设计

1. 前面锁库存模块也已经提到过,DB可售sq锁到lq,扣减SQL层直接通过 set sq = sq -δq where sq-lq - δq> 0 的方式保证DB不超卖。

2. 而Redis扣减,分桶的剩余库存值不能小于0,保证不超卖;同时如果Redis数据丢了,实际这个Redis分桶库存扣减了多少,以DB合并下单明细为准,也不会少卖。

3. 高并发场景下,下单扣减和合并提交单据扫描并发的情况下,可能会出现少扫描了单据,如果少扫描单据了,则会超卖;我们通过Redis扣减屏障防止此并发场景下的超卖;具体链路如下:

自动锁库存模块

通过热点识别能力,感知交易相关系统的热点品库存查询,提前将满足条件的商品库存进行自动锁库存;只要一开始提前锁了任意数量的库存,后续下单扣减过程中,则会继续自动触发锁库存。

性能优化效果

接下来我们看下合并扣减的效果:

压测效果

从压测效果上看,单行热点扣减场景下,非合并扣减tps峰值约1.4w,合并扣减峰值3.2w;整体提升1倍以上扣减性能(实际线上场景,由于入口做了限流,所以线上达不到压测的流量峰值)。

线上效果

线上由于入口开了限流,库存扣减峰值tps都在7k+,差异体现在扣减成功率和耗时上:

走到合并扣减的热点品,库存扣减成功率在100%,接口扣减耗时7ms+;未走到合并扣减的热点品,库存扣减成功率在80%上下,接口扣减耗时15ms+。


结语

以上则是基于分布式缓存的强一致库存扣减方案的全部内容,欢迎感兴趣的同学一起交流。


来源  |  阿里云开发者公众号

作者  |  聆枫

相关文章
|
4月前
|
SQL 人工智能 分布式计算
从工单、文档到结构化知识库:一套可复用的 Agent 知识采集方案
我们构建了一套“自动提取 → 智能泛化 → 增量更新 → 向量化同步”的全链路自动化 pipeline,将 Agent 知识库建设中的收集、提质与维护难题转化为简单易用的 Python 工具,让知识高效、持续、低门槛地赋能智能体。
999 36
|
5月前
|
缓存 NoSQL Redis
千万级数据表的count(*)查询优化
针对千万级数据表`user_factor_auth_record`的COUNT查询性能问题,可通过“避免实时计数、独立计数表、Redis缓存”三大方案优化。优先从业务层面取消总条数展示,减轻数据库压力;若需精确值,可借助事务维护独立计数表,或定时缓存至Redis,分摊开销、提升查询效率。
400 5
|
NoSQL Java 关系型数据库
秒杀场景下如何保证数据一致性?就这个问题我给出了最详细的方案
本文主要讨论秒杀场景的解决方案。 什么是秒杀? 从字面意思理解,所谓秒杀,就是在极短时间内,大量的请求涌入,处理不当时容易出现服务崩溃或数据不一致等问题的高并发场景。 常见的秒杀场景有淘宝双十一、网约车司机抢单、12306抢票等等。
|
29天前
|
人工智能 安全 API
深度解析 Claude Code 在 Prompt / Context / Harness 的设计与实践
文章内容基于作者个人技术实践与独立思考,旨在分享经验,仅代表个人观点。
2775 75
深度解析 Claude Code 在 Prompt / Context / Harness 的设计与实践
|
1月前
|
人工智能 安全 API
学习笔记:从 Agent 到 Skills — AI 智能体架构的范式转变
报告日期:2026-02-28 关键词: Agent Skills, MCP, OpenClaw, A2A, Agentic AI, 模块化架构
学习笔记:从 Agent 到 Skills — AI 智能体架构的范式转变
|
7月前
|
设计模式 Java Spring
Java 设计模式之责任链模式:优雅处理请求的艺术
责任链模式通过构建处理者链,使请求沿链传递直至被处理,实现发送者与接收者的解耦。适用于审批流程、日志处理等多级处理场景,提升系统灵活性与可扩展性。
805 2
|
2月前
|
消息中间件 缓存 NoSQL
秒杀系统高并发核心优化与落地全指南
本文系统阐述秒杀系统架构设计:剖析瞬时高并发、库存超卖等核心痛点,提出漏斗过滤、读写分离、强一致性等设计原则;详解前端、Nginx、网关、业务、缓存、消息队列及数据库七层优化方案;并给出Redis预扣减+异步落库等生产级解决方案与完整代码实现。
502 3
|
5月前
|
人工智能 网络协议 Java
一文带你玩转 WebSocket 全链路可观测
在 AI 实时交互爆发的时代,WebSocket 成为核心协议。但其双向、长连接、流式传输特性,让传统链路追踪频频失效。阿里云 LoongSuite 基于 OpenTelemetry 标准,结合探针增强与自定义扩展,首次实现 WebSocket 全链路可观测,支持 Span 粒度控制、上下文透传、异步衔接与关键性能指标采集。
777 88

热门文章

最新文章