《面试官:说说电商库存扣减如何防超卖?分布式锁的三种实现》

简介: 这是一道电商高频面试题,聚焦库存防超卖与分布式锁实战。提供可直接背诵的“三段式”方案(Redis Lua预扣 + MQ异步 + DB乐观锁),详解Redis/ZK/DB三种锁原理、源码级实现、避坑指南及大厂选型逻辑,兼顾技术深度与落地能力。(239字)

这是一个经典的电商高频面试题,也是很多同学容易“背概念、缺落地”的地方。下面我给你一套面试可直接背诵 + 技术深度拉满的回答模板,同时补齐源码级原理和避坑指南。

🛒 面试官:说说电商库存扣减如何防超卖?分布式锁的三种实现

面试核心思路:先说业务模型 → 再说并发问题 → 给出三种锁方案 → 对比选型 → 兜底方案

一、先给结论(一句话版)

防超卖的本质:保证库存扣减的“原子性 + 幂等性”。

实践中通常采用:

Redis 原子扣减(Lua) + 数据库乐观锁 + 分布式锁兜底。

二、超卖是怎么发生的?(面试官爱问)

典型并发场景

库存 = 1
线程A:查库存 = 1 ✅
线程B:查库存 = 1 ✅
线程A:扣减库存 → 0 ✅
线程B:扣减库存 → -1 ❌(超卖)

根本原因

问题 说明

非原子操作 select → judge → update 被拆分

并发竞争 多线程同时进入临界区

重试机制 MQ 重试导致重复扣减

三、正确的库存扣减模型(重点)

✅ 强烈推荐的“三段式”模型

1️⃣ Redis 预扣(高性能、抗并发)
2️⃣ MQ 异步落库(解耦、削峰)
3️⃣ DB 最终扣减(强一致)

核心原则

✅ 不要直接在高并发下操作 MySQL
✅ 所有写库存必须是原子操作
✅ 任何扣减都必须幂等

四、分布式锁的三种实现(面试正菜)

✅ 方案一:Redis 分布式锁(最常用)

1️⃣ 加锁(SET NX PX)

SET lock:stock:1001 uuid NX PX 30000

2️⃣ 解锁(Lua 保证原子性)

if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end

3️⃣ Java 示例(简化版)

String uuid = UUID.randomUUID().toString();
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent("lock:stock:1001", uuid, 30, TimeUnit.SECONDS);

if (locked) {
try {
deductStock(); // 核心业务
} finally {
unlock(uuid);
}
}

✅ 优点

• 性能极高(内存级)

• 实现简单

• 适合秒杀、高并发

❌ 缺点

• 主从切换可能丢锁(CAP)

• 锁过期 ≠ 业务执行完成

✅ 解决方案

• Redisson Watch Dog(自动续期)

• RedLock(多实例)

✅ 方案二:ZooKeeper 分布式锁(最稳)

原理

/lock
├── /order-000001(临时顺序节点)
├── /order-000002
└── /order-000003

• 谁序号最小,谁拿到锁

• 客户端断连 → 节点自动删除 → 锁释放

Curator 示例

InterProcessMutex lock =
new InterProcessMutex(client, "/lock/stock");

lock.acquire();
try {
deductStock();
} finally {
lock.release();
}

✅ 优点

• CP 强一致

• 不会误删锁

• 天然防死锁

❌ 缺点

• 性能不如 Redis

• 运维复杂

✅ 使用场景

• 资金类

• 订单创建

• 核心配置变更

✅ 方案三:数据库锁(最朴素但有效)

方式 1️⃣:悲观锁(不推荐)

SELECT * FROM stock WHERE id=1 FOR UPDATE;

❌ 缺点:
• 连接被长时间占用

• 极易拖垮数据库

✅ 方式 2️⃣:乐观锁(强烈推荐)

UPDATE stock
SET quantity = quantity - 1,
version = version + 1
WHERE id = 1
AND quantity >= 1
AND version = old_version;

Java 判定

if (rows == 0) {
throw new RuntimeException("库存扣减失败");
}

✅ 优点

• 无锁、高并发

• 天然防超卖

• 数据库兜底方案

❌ 缺点

• 重试成本高

• 不适合秒杀级流量

五、三种分布式锁对比(面试必背)

维度 Redis ZooKeeper 数据库

性能 ⭐⭐⭐⭐ ⭐⭐ ⭐

一致性 AP CP CP

实现难度 中 高 低

死锁风险 有 无 无

典型场景 秒杀、缓存 核心业务 低频扣减

六、真正的大厂方案(加分项)

✅ Redis Lua 原子扣减(无锁胜有锁)

local stock = tonumber(redis.call("get", KEYS[1]))
if stock >= tonumber(ARGV[1]) then
return redis.call("decrby", KEYS[1], ARGV[1])
else
return -1
end

👉 面试金句:

“在高并发库存扣减场景中,我们优先使用 Redis Lua 原子操作,只有在复杂业务或幂等控制时才引入分布式锁。”

七、完整面试标准答案(可直接背)

面试官:说说电商库存扣减如何防超卖?

✅ 标准回答模板:

防超卖的核心是原子性 + 幂等性。

我们一般分三层来做:

第一层,在 Redis 中用 Lua 脚本做原子扣减,保证高并发下不超卖;

第二层,通过 MQ 异步将扣减请求发到下游,由消费者用数据库乐观锁完成最终扣减;

第三层,为了防止极端并发和重复请求,我们会使用 分布式锁。

分布式锁我们有三种实现:

1️⃣ Redis 锁:性能好,适合秒杀,但要注意锁续期和主从切换问题;

2️⃣ ZooKeeper 锁:强一致,适合资金、订单等核心业务;

3️⃣ 数据库乐观锁:通过 version 或 CAS 更新,作为最终兜底。

在实际生产中,我们更倾向 Redis + Lua + 乐观锁 的组合方案。

八、面试官追问准备

Q:Redis 锁过期了,业务还没执行完怎么办?

✅ 答:Redisson 的 Watch Dog 自动续期。

Q:Redis 主从切换锁丢了怎么办?

✅ 答:使用 RedLock 或多实例仲裁。

Q:MQ 重复消费会不会超卖?

✅ 答:订单号 / 流水号 幂等校验。

九、总结一句话

防超卖不是靠一把锁,而是靠“原子操作 + 幂等设计 + 分层兜底”。
以上是我在电商中台领域的一些实践,目前我正在这个方向进行更深入的探索/提供相关咨询与解决方案。如果你的团队有类似的技术挑战或合作需求,欢迎通过[我的GitHub/个人网站/邮箱]与我联系
你想继续哪一个?

相关文章
|
9月前
|
缓存 NoSQL 测试技术
库存合并扣减:一种基于分布式缓存的强一致性热点库存扣减方案
本文介绍了一种基于Redis分桶扣减与DB合并提交的强一致库存扣减方案,适用于热点商品高并发抢购场景。通过Redis实现高性能扣减计数,结合数据库明细保障数据准确,既避免超卖少卖,又显著提升TPS与系统稳定性,有效支撑直播等大流量业务需求。
库存合并扣减:一种基于分布式缓存的强一致性热点库存扣减方案
|
SQL 消息中间件 缓存
库存扣减问题
库存扣减问题
库存扣减问题
|
SQL 关系型数据库 数据库
学习分布式事务Seata看这一篇就够了,建议收藏
学习分布式事务Seata看这一篇就够了,建议收藏
26329 2
|
7月前
|
关系型数据库 MySQL Java
分布式事务终极指南:2PC/XA/TCC/SAGA 从底层原理到生产选型全拆解
本文系统解析分布式事务四大主流方案:XA/2PC(强一致但性能差)、TCC(高并发柔性事务)、SAGA(长事务最终一致)及理论基石(ACID/CAP/BASE),涵盖原理、流程、实战代码、优劣对比与生产选型标准,助你深入掌握核心逻辑。
1385 3
原理篇:Seata TCC模式是如何解决幂等性、资源悬挂、空回滚问题的
原理篇:Seata TCC模式是如何解决幂等性、资源悬挂、空回滚问题的
2025 0
|
5月前
|
算法 关系型数据库 MySQL
【MySQL】MySQL的海量数据处理六大方案:分库分表、读写分离、分片策略、跨库事务、扩容方案、Sharding-JDBC中间件
本文系统梳理MySQL海量数据处理六大核心方案:读写分离、垂直/水平分库分表、分片策略选型、分布式事务(2PC/TCC/Saga等)、平滑扩容实践及Sharding-JDBC中间件应用,兼顾性能、一致性与可扩展性,助力架构稳健演进。
|
6月前
|
SQL 存储 关系型数据库
干掉 90% 慢 SQL!MySQL 全链路排查与优化方法论,从执行计划到表结构全拆解
本文系统讲解MySQL慢SQL优化全链路方法:从慢查询日志精准定位、EXPLAIN执行计划深度解析,到10大索引失效场景根因拆解、8大SQL改写实战技巧;涵盖表结构设计规范与Java层防控实践,强调“先定位、再看执行计划、后优化”,助力开发者高效解决80%以上数据库性能瓶颈。
1195 1
|
7月前
|
XML Java 数据安全/隐私保护
彻底搞懂 Spring Boot 自动配置原理:从源码拆解到手写 Starter,零废话全干货
本文深入解析SpringBoot自动配置原理,基于SpringBoot 3.4.2版本详细拆解了自动配置的执行流程。主要内容包括:1)自动配置的本质是基于条件注解的动态JavaConfig配置类;2)核心执行流程通过AutoConfigurationImportSelector实现;3)SpringBoot 3.x采用新的自动配置注册方式;4)重点讲解了@Conditional系列条件注解的使用场景与常见坑点;5)通过开发自定义加密Starter实战演示完整实现过程。
2037 3
|
消息中间件 缓存 算法
G1原理—10.如何优化G1中的FGC
本文主要探讨G1垃圾回收器中的FGC(Full GC)优化问题,分析其触发原因及优化策略。首先,通过一个实际案例(Kafka发送重试+`subList`导致List不断增大)展示FGC的产生过程及其影响。其次,对比G1与ParNew + CMS的FGC触发机制,指出G1的FGC更严重的原因,包括更大的堆内存管理需求、苛刻的触发条件以及复杂的RSet处理。最后,介绍FGC相关参数优化思路,如调整`-XX:G1HeapRegionSize`、`-XX:G1ReservePercent`等参数,以提升垃圾回收效率,减少FGC的发生频率。总结强调避免FGC的核心在于让垃圾回收速度匹配垃圾产生速度。
G1原理—10.如何优化G1中的FGC
|
缓存 NoSQL 算法
高并发秒杀系统实战(Redis+Lua分布式锁防超卖与库存扣减优化)
秒杀系统面临瞬时高并发、资源竞争和数据一致性挑战。传统方案如数据库锁或应用层锁存在性能瓶颈或分布式问题,而基于Redis的分布式锁与Lua脚本原子操作成为高效解决方案。通过Redis的`SETNX`实现分布式锁,结合Lua脚本完成库存扣减,确保操作原子性并大幅提升性能(QPS从120提升至8,200)。此外,分段库存策略、多级限流及服务降级机制进一步优化系统稳定性。最佳实践包括分层防控、黄金扣减法则与容灾设计,强调根据业务特性灵活组合技术手段以应对高并发场景。
3977 7

热门文章

最新文章