
1) 【一句话结论】:设计库存管理系统时,需构建包含商品、仓库、库存(带版本号、冻结标记)、订单、日志的表,通过订单确认后冻结库存(事务隔离或分布式锁)结合乐观锁处理并发扣减,确保库存数据准确,避免超卖。
2) 【原理/概念讲解】:老师口吻,解释核心表与机制:
3) 【对比与适用场景】:
| 方式 | 定义 | 特性 | 使用场景 | 注意点 |
|---|---|---|---|---|
| 库存冻结(订单确认后) | 订单确认时锁定库存(事务隔离或分布式锁) | 严格防超卖,写操作需等待冻结解除 | 订单确认后扣减库存(如发货前) | 可能导致订单延迟,需合理设置冻结时间 |
| 乐观锁(版本号) | 更新时检查版本号是否匹配 | 低锁竞争,高并发下性能好,读多写少 | 读多写少,频繁查询库存状态 | 版本号回滚或重试,可能因重试导致延迟 |
| 悲观锁(行级锁) | 用锁控制并发 | 严格保证一致性,写多读少 | 写多读少,紧急补货等关键操作 | 可能导致死锁,锁竞争高 |
4) 【示例】:
-- 商品表
CREATE TABLE 商品表 (
商品ID INT PRIMARY KEY,
商品名称 VARCHAR(100),
单位 VARCHAR(20),
标准库存 INT
);
-- 仓库表
CREATE TABLE 仓库表 (
仓库ID INT PRIMARY KEY,
仓库名称 VARCHAR(50),
地址 VARCHAR(100)
);
-- 库存表(核心表,带版本号和冻结标记)
CREATE TABLE 库存表 (
库存ID INT PRIMARY KEY,
商品ID INT,
仓库ID INT,
货位ID VARCHAR(50),
当前库存 INT,
版本号 INT DEFAULT 1,
冻结标记 TINYINT DEFAULT 0,
创建时间 TIMESTAMP,
FOREIGN KEY (商品ID) REFERENCES 商品表(商品ID),
FOREIGN KEY (仓库ID) REFERENCES 仓库表(仓库ID)
);
-- 订单表
CREATE TABLE 订单表 (
订单ID INT PRIMARY KEY,
订单类型 VARCHAR(20), -- '采购'/'销售'
创建时间 TIMESTAMP,
关联订单号 VARCHAR(50)
);
-- 库存日志表(审计)
CREATE TABLE 库存日志表 (
日志ID INT PRIMARY KEY,
操作类型 VARCHAR(10), -- '冻结'/'扣减'/'补货'
商品ID INT,
仓库ID INT,
操作前库存 INT,
操作后库存 INT,
订单ID INT,
操作时间 TIMESTAMP,
操作人 VARCHAR(50)
);
public void 扣减库存(int 订单ID, int 商品ID, int 仓库ID, int 扣减量) {
freezeStock(商品ID, 仓库ID); // 订单确认后冻结库存
int 重试次数 = 0;
while (重试次数 < MAX_RETRY) {
try {
Stock stock = stockRepository.findById(商品ID, 仓库ID);
if (stock == null || stock.冻结标记 != 1) {
throw new RuntimeException("库存未冻结或不存在");
}
int affectedRows = stockRepository.updateStock(
商品ID, 仓库ID, stock.当前库存 - 扣减量, stock.版本号 + 1
);
if (affectedRows == 1) {
logRepository.addLog(商品ID, 仓库ID, stock.当前库存, stock.当前库存 - 扣减量, 订单ID, "扣减");
unfreezeStock(商品ID, 仓库ID); // 解冻
return;
}
} catch (Exception e) {
重试次数++;
if (重试次数 >= MAX_RETRY) {
throw new RuntimeException("库存扣减失败", e);
}
}
}
}
private void freezeStock(int 商品ID, int 仓库ID) {
stockRepository.updateStockWithLock(商品ID, 仓库ID, null, null, 1); // 设置冻结标记为1
}
private void unfreezeStock(int 商品ID, int 仓库ID) {
stockRepository.updateStock(商品ID, 仓库ID, null, null, 0); // 解冻
}
5) 【面试口播版答案】:
面试官您好,针对南光集团进出口商品的仓储库存管理,我设计如下:首先,数据库表结构包括商品表(存储商品基本信息)、仓库表(仓库信息)、库存表(核心表,记录每个货位的实时库存、版本号和冻结标记,用于乐观锁和库存锁定)、订单表(业务订单)、库存日志表(审计)。处理并发扣减时,采用订单确认后冻结库存(通过事务隔离级别SERIALIZABLE锁定库存,防止超卖)结合乐观锁机制:扣减库存时,先检查库存是否已冻结,然后查询库存表获取当前库存和版本号,执行更新语句(检查版本号是否一致,不一致则重试),这样能保证多个订单同时扣减同一库存时,数据一致性。具体流程是,订单确认后,先冻结库存(设置冻结标记为1),然后扣减库存(乐观锁检查),扣减成功后解冻,确保库存数据准确,避免超卖问题。
6) 【追问清单】:
7) 【常见坑/雷区】: