《淘宝双11同款:基于 Sentinel 的微服务流量防卫兵实战》

简介: 电商库存防超卖是高并发核心面试题,本文详解超卖成因、三级防护(Redis预扣+Lua原子操作+DB乐观锁)、三种分布式锁对比及幂等设计,兼顾技术深度与面试表达。

这个问题是电商面试中的高频题,涉及业务一致性与分布式系统核心技术。下面我将为你撰写一篇结构清晰、技术深入的文章,既适合学习,也适合面试准备。

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

“秒杀不到,库存没了;秒杀到了,库存却超了。” —— 这是电商最怕的事故之一。

在电商系统中,库存扣减是核心业务,一旦出错,轻则用户投诉,重则资损赔偿。而高并发场景下,如何防止“超卖”(Over-Selling)?答案离不开:分布式锁 + 原子操作 + 幂等性设计。

本文将深入讲解:

  1. 超卖是如何发生的?
  2. 如何设计安全的库存扣减流程?
  3. 三种主流分布式锁实现方式对比
  4. 面试加分项:Redisson、ZooKeeper、数据库锁

一、什么是“超卖”?

超卖:实际卖出的商品数量 > 库存数量。

示例场景:

• 商品库存:10 件

• 同时 15 人下单成功 → 超卖 5 件

• 结果:无法发货,引发客诉、退款、平台信誉受损

二、超卖的根本原因

原因 说明

并发读写未加锁 多个线程同时读取库存为 10,各自扣减,最终扣成负数

非原子操作 查询库存 + 判断 + 扣减 三步分离,中间被插队

缓存与DB不一致 Redis 预扣库存,但 DB 更新失败,未回滚

重试机制不当 消息重试导致重复扣减

三、正确库存扣减流程设计

✅ 核心原则:原子性 + 幂等性 + 最终一致性

推荐流程(以秒杀为例):

  1. 用户点击购买 → 进入排队队列
  2. 检查库存(Redis 预扣)
  3. 扣减 Redis 库存(Lua 脚本保证原子性)
  4. 发送 MQ 消息 → 异步扣减数据库
  5. 返回“抢购中”状态
  6. 消费者消费消息 → 扣减 DB 库存
  7. 扣减成功 → 通知用户付款
  8. 超时未付款 → 回补库存(Redis + DB)

⚠️ 关键点:所有“读-改-写”操作必须原子化,不能拆开!

四、分布式锁的三种实现方式

问:如何保证同一时间只有一个请求能扣减某商品的库存?

答:用分布式锁,把“扣减”变成串行操作。

方式一:基于 Redis 的分布式锁(最常用)

实现方式:SET key value NX PX 30000

// 加锁
String lockKey = "stock:lock:" + productId;
String requestId = UUID.randomUUID().toString();
Boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, requestId, 30, TimeUnit.SECONDS);

if (locked) {
try {
// 执行业务:扣减库存
int remain = stockService.deductStock(productId, quantity);
if (remain < 0) throw new RuntimeException("库存不足");
} finally {
// 释放锁:Lua 保证只有自己能删
String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(lockKey), requestId);
}
} else {
throw new RuntimeException("系统繁忙,请重试");
}

✅ 优点:

• 高性能(内存操作)

• 支持过期自动释放

• 广泛使用(Redisson 封装)

❌ 缺点:

• 主从切换可能导致锁失效(需用 RedLock)

• 时间漂移问题(锁过期但任务未完成)

方式二:基于 ZooKeeper 的分布式锁(高可靠)

原理:临时顺序节点 + Watch 机制

// Curator 框架示例
InterProcessMutex lock = new InterProcessMutex(client, "/locks/stock_" + productId);
try {
if (lock.acquire(10, TimeUnit.SECONDS)) {
// 扣减库存
stockMapper.decrease(productId, quantity);
}
} finally {
lock.release();
}

✅ 优点:

• 强一致性(ZAB 协议)

• 锁自动释放(会话断开即删除节点)

• 公平锁支持好

❌ 缺点:

• 性能低于 Redis

• 运维成本高(需部署 ZK 集群)

方式三:基于数据库的分布式锁(最简单,但不推荐高并发)

方式 A:唯一索引 + INSERT 抢占

CREATE TABLE distributed_lock (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
lock_key VARCHAR(64) NOT NULL UNIQUE,
owner_id VARCHAR(64),
expire_time DATETIME
);

抢占锁:
INSERT INTO distributed_lock (lock_key, owner_id, expire_time)
VALUES ('stock_123', 'user_456', NOW() + INTERVAL 30 SECOND);
-- 主键冲突则抢占失败

释放锁:DELETE FROM distributed_lock WHERE lock_key='stock_123' AND owner_id='user_456'

方式 B:UPDATE 乐观锁(更推荐)

UPDATE stock SET quantity = quantity - #{quantity}, version = version + 1
WHERE product_id = #{productId} AND quantity >= #{quantity};

配合版本号或 CAS 操作,天然防超卖。

✅ 优点:

• 无需额外中间件

• 简单易懂,适合中小项目

❌ 缺点:

• 性能差(DB 瓶颈)

• 锁持有时间长会阻塞连接池

• 不支持非阻塞获取

五、三种锁对比表(面试必备)

特性 Redis 锁 ZooKeeper 锁 数据库锁

一致性 最终一致 强一致 强一致

性能 ⭐⭐⭐⭐ ⭐⭐ ⭐

可靠性 中(需 RedLock) 高 低

实现复杂度 中 高 低

适用场景 秒杀、缓存击穿防护 金融、分布式协调 低频扣减、简单业务

推荐框架 Redisson Curator MyBatis + SQL

六、进阶:如何用 Lua + Redis 彻底避免超卖?(无锁胜有锁)

原子脚本示例(Redis 中执行):

-- KEYS[1]: stock:{productId}
-- ARGV[1]: 扣减数量

local stock = tonumber(redis.call('GET', KEYS[1]))
if stock >= tonumber(ARGV[1]) then
redis.call('DECRBY', KEYS[1], ARGV[1])
return stock - tonumber(ARGV[1])
else
return -1 -- 库存不足
end

Java 调用:
DefaultRedisScript script = new DefaultRedisScript<>(luaScript, Long.class);
Long remain = redisTemplate.execute(script, Collections.singletonList(key), String.valueOf(quantity));
if (remain != null && remain >= 0) {
// 扣减成功,继续下单流程
}

👉 优势:无需分布式锁,单条命令完成判断+扣减,天然原子!

七、面试加分回答模板

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

你可以这样回答:

“我们采用‘三级防护’机制:

  1. 前端限流 + 按钮置灰,减少无效请求;
  1. Redis 预扣库存,通过 Lua 脚本保证原子性,避免并发超卖;

  2. 数据库最终扣减,使用乐观锁(version 或 CAS)确保数据一致;

  3. 为防止重复扣减,我们引入幂等性设计,通过订单号 + 状态机控制;

  4. 在高并发场景下,我们使用 Redisson 分布式锁 对关键资源加锁,确保同一时间只有一个请求能操作某商品库存。

此外,我们还设计了库存回补机制:用户超时未付款,通过定时任务将 Redis 和 DB 库存同步回补。”

八、避坑指南

坑 解决方案

锁忘记释放 用 try-finally 或看门狗机制(Redisson)

锁过期但任务没完 设置合理的过期时间 + 看门狗续期

主从切换丢锁 使用 RedLock 或 ZooKeeper

缓存与 DB 不一致 采用“Cache-Aside + 消息补偿”模式

重复扣减 订单号幂等校验 + 状态机(待支付→已支付)

九、总结

技术点 作用

分布式锁 控制并发,避免同时操作

原子操作(Lua) 替代锁,提升性能

乐观锁(CAS) 保证 DB 层不超卖

幂等性 防止重复扣减

最终一致性 通过 MQ 异步同步数据

十、推荐学习资源

• Redisson 官方文档:https://redisson.org/

• Curator 锁实现:https://curator.apache.org/

• 阿里《Java 开发手册》:关于锁的使用规范

• 极客时间《分布式技术原理与实战》

需要我为你画一个“库存防超卖全链路流程图”,用 Mermaid 或文字描述,方便你在面试中手绘展示吗?这样可以让回答更直观,提升印象分。

相关文章
|
2天前
|
人工智能 JSON 机器人
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
本文带你零成本玩转OpenClaw:学生认证白嫖6个月阿里云服务器,手把手配置飞书机器人、接入免费/高性价比AI模型(NVIDIA/通义),并打造微信公众号“全自动分身”——实时抓热榜、AI选题拆解、一键发布草稿,5分钟完成热点→文章全流程!
10228 33
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
|
14天前
|
人工智能 安全 Linux
【OpenClaw保姆级图文教程】阿里云/本地部署集成模型Ollama/Qwen3.5/百炼 API 步骤流程及避坑指南
2026年,AI代理工具的部署逻辑已从“单一云端依赖”转向“云端+本地双轨模式”。OpenClaw(曾用名Clawdbot)作为开源AI代理框架,既支持对接阿里云百炼等云端免费API,也能通过Ollama部署本地大模型,完美解决两类核心需求:一是担心云端API泄露核心数据的隐私安全诉求;二是频繁调用导致token消耗过高的成本控制需求。
5907 14
|
21天前
|
人工智能 JavaScript Ubuntu
5分钟上手龙虾AI!OpenClaw部署(阿里云+本地)+ 免费多模型配置保姆级教程(MiniMax、Claude、阿里云百炼)
OpenClaw(昵称“龙虾AI”)作为2026年热门的开源个人AI助手,由PSPDFKit创始人Peter Steinberger开发,核心优势在于“真正执行任务”——不仅能聊天互动,还能自动处理邮件、管理日程、订机票、写代码等,且所有数据本地处理,隐私完全可控。它支持接入MiniMax、Claude、GPT等多类大模型,兼容微信、Telegram、飞书等主流聊天工具,搭配100+可扩展技能,成为兼顾实用性与隐私性的AI工具首选。
23145 119
|
7天前
|
人工智能 JavaScript API
解放双手!OpenClaw Agent Browser全攻略(阿里云+本地部署+免费API+网页自动化场景落地)
“让AI聊聊天、写代码不难,难的是让它自己打开网页、填表单、查数据”——2026年,无数OpenClaw用户被这个痛点困扰。参考文章直击核心:当AI只能“纸上谈兵”,无法实际操控浏览器,就永远成不了真正的“数字员工”。而Agent Browser技能的出现,彻底打破了这一壁垒——它给OpenClaw装上“上网的手和眼睛”,让AI能像真人一样打开网页、点击按钮、填写表单、提取数据,24小时不间断完成网页自动化任务。
1897 4

热门文章

最新文章