全栈面试实战:从商品详情到可靠下单,如何设计库存扣减链路?
作者:王文峰(神秘小峯 / 山峯)|专注 Java、全栈开发与业务系统实战
全栈面试实战:从商品详情到可靠下单,如何设计库存扣减链路?
- 全栈面试实战:从商品详情到可靠下单,如何设计库存扣减链路?
-
- 一、面试题与业务背景
-
- 面试官提问
- 业务场景
- 目标与边界
- 二、先给结论:把正确性放在数据库边界内
- 三、核心流程拆解
-
- 1. 商品详情页:展示价格,但提交时重新校验
- 2. API 契约:让重试、错误和订单状态可表达
- 3. 服务端:用条件更新处理并发扣减
- 4. 事务到底保证什么
- 5. 支付回调:按状态机和事件幂等处理
- 四、缓存、消息队列与演进
-
- 库存缓存适合做什么
- 消息队列适合做什么
- 五、异常场景与验证
- 六、方案取舍与上线关注点
- 七、面试官可能继续追问
- 八、总结
- 九、把业务问题转化为需求清单
-
- 1. 先识别参与者
- 2. 明确业务对象
- 3. 功能需求
- 4. 非功能需求
- 5. 需求澄清问题
- 6. 需求与设计的映射
- 7. 需求优先级
- 十、端到端架构与模块边界
-
- 1. 逻辑组件
- 2. 单体起步时的边界
- 3. 请求链路
- 4. 不同职责不要混在 Controller
- 5. 同步与异步边界
- 6. 一致性边界
- 7. 系统上下文图
- 8. 容量估算的输入
- 十一、数据模型深入设计
-
- 1. 商品与 SKU
- 2. 库存按仓库建模
- 3. 可售库存的定义
- 4. 库存流水
- 5. 为什么需要库存流水
- 6. 订单头与订单明细
- 7. 金额精度
- 8. 订单状态字段
- 9. 软删除与历史数据
- 10. 索引设计原则
- 11. 外键取舍
- 12. 数据迁移策略
- 十二、Java 服务端分层实现
-
- 1. 请求 DTO
- 2. Controller 示例
- 3. 输入校验
- 4. 应用服务
- 5. 仓储接口
- 6. MyBatis 更新语句
- 7. 事务传播与代理
- 8. 异常映射
- 9. 订单号生成
- 10. 幂等冲突处理
- 十三、价格、优惠与订单金额
-
- 1. 价格来源
- 2. 价格快照
- 3. 优惠券并发
- 4. 优惠锁定时机
- 5. 价格变化时的用户体验
- 十四、库存策略对比与并发控制
-
- 1. 直接扣减
- 2. 预占库存
- 3. 条件更新
- 4. 悲观锁
- 5. 乐观锁
- 6. 多 SKU 批量下单
- 7. 死锁处理
- 8. 热门 SKU
- 9. 库存流水与余额核对
- 10. 库存回补必须幂等
- 十五、订单状态机与状态迁移
-
- 1. 状态定义示例
- 2. 状态迁移表
- 3. 状态更新要有前置条件
- 4. 状态与副作用
- 5. 状态机测试
- 十六、取消订单与超时关闭
-
- 1. 用户取消
- 2. 超时任务
- 3. 条件关闭
- 4. 批量扫描
- 5. 支付临界时序
- 十七、支付适配与回调安全
-
- 1. 支付请求流程
- 2. 回调校验
- 3. 回调幂等
- 4. 回调响应时机
- 5. 主动查询
- 6. 退款与部分退款
- 7. 密钥管理
- 十八、Outbox 与可靠事件发布
-
- 1. 问题窗口
- 2. Outbox 表
- 3. 写入流程
- 4. 并发发布器
- 5. 消费者幂等
- 6. 消息版本兼容
- 7. 重试与死信
- 十九、前端工程实现细节
-
- 1. 页面状态建模
- 2. 幂等键的客户端生命周期
- 3. 避免重复请求的多层措施
- 4. 错误展示
- 5. 可访问性
- 6. 表单校验
- 7. 防止跨站请求与令牌泄漏
- 8. 前端埋点
- 二十、API 设计与兼容性
-
- 1. HTTP 状态码
- 2. 错误响应结构
- 3. 订单查询接口
- 4. 列表分页
- 5. API 版本
- 6. 幂等键协议
- 二十一、安全与隐私
-
- 1. 身份认证
- 2. 授权
- 3. 输入防护
- 4. 重放攻击
- 5. 速率限制
- 6. 敏感信息
- 7. 审计日志
- 8. 权限测试
- 二十二、测试策略
-
- 1. 单元测试
- 2. Repository 集成测试
- 3. API 集成测试
- 4. 契约测试
- 5. 并发测试
- 6. 故障注入
- 7. 属性测试
- 8. 测试数据管理
- 9. 压力测试指标
- 10. 压测结果解释
- 二十三、可观测性与生产运维
-
- 1. 结构化日志字段
- 2. 关键指标
- 3. 告警原则
- 4. 链路追踪
- 5. 运行手册
- 6. 数据修复
- 二十四、部署、灰度与回滚
-
- 1. 配置管理
- 2. 数据库变更顺序
- 3. 灰度策略
- 4. 回滚边界
- 5. 发布后核验
- 二十五、性能优化的先后顺序
-
- 1. 先测量再优化
- 2. 数据库查询
- 3. 连接池
- 4. 缓存商品读取
- 5. 限流与排队
- 6. 分区与拆分
- 二十六、常见错误设计及其修正
-
- 错误一:前端计算总价并直接提交
- 错误二:先查库存再减库存
- 错误三:按钮禁用就认为请求幂等
- 错误四:把支付接口放进数据库事务
- 错误五:支付回调收到就直接改成已支付
- 错误六:消息只发送一次
- 错误七:所有异常都无限重试
- 错误八:为了高并发先上复杂架构
- 错误九:用订单号难猜代替授权
- 错误十:只看接口 200,不查业务不变量
- 二十七、方案演进路线
-
- 阶段一:单体与单库
- 阶段二:异步副作用
- 阶段三:拆分支付或商品服务
- 阶段四:热点库存治理
- 阶段五:多区域能力
- 二十八、面试回答的组织方式
-
- 1. 开场先确认约束
- 2. 先画核心链路
- 3. 选择两个重点深入
- 4. 主动讲失败路径
- 5. 讲取舍而非背组件
- 6. 量化验证方法
- 7. 一分钟回答示例
- 8. 面试中容易过度承诺的说法
- 二十九、面试官追问扩展题
-
- 1. 如果同一订单有多个 SKU,如何避免部分扣减?
- 2. 如果订单和库存分属不同数据库怎么办?
- 3. 幂等键保存多久?
- 4. 相同幂等键不同请求为什么不能直接返回旧订单?
- 5. 为什么请求摘要不能包含非确定字段?
- 6. 如何处理锁等待超时?
- 7. 订单号生成器故障怎么办?
- 8. 数据库主从延迟会影响幂等读取吗?
- 9. 缓存读到库存为零但数据库有货怎么办?
- 10. 如何避免超时关闭任务重复释放库存?
- 11. 如何处理支付平台回调乱序?
- 12. 消息积压时下单接口是否应该失败?
- 13. 如何确认订单没有丢失?
- 14. 如果用户重复支付怎么办?
- 15. 如何防止越权取消订单?
- 16. 数据库事务隔离级别选什么?
- 17. 为什么库存检查 SQL 的数量必须校验为正数?
- 18. 为什么要求库存字段非负约束?
- 19. 金额比较如何避免精度错误?
- 20. 怎样处理订单查询的隐私?
- 三十、示例项目目录与职责
-
- 1. API 包
- 2. Application 包
- 3. Domain 包
- 4. Infrastructure 包
- 5. 测试包
- 三十一、订单创建伪代码逐步推演
-
- 1. 接收请求
- 2. 规范化请求
- 3. 查询幂等结果
- 4. 开启事务
- 5. 获取服务端报价
- 6. 占用库存
- 7. 创建订单快照
- 8. 写入库存流水
- 9. 写入 Outbox
- 10. 提交事务
- 11. 返回结果
- 12. 后续副作用
- 三十二、系统不变量清单
- 三十三、数据库事务与锁的注意事项
-
- 1. 事务长度
- 2. 锁顺序
- 3. 隔离级别
- 4. 唯一约束
- 5. 自增主键与热点
- 6. 读写分离
- 7. 连接超时与事务不确定性
- 三十四、部署环境和配置检查
-
- 1. 环境隔离
- 2. 超时预算
- 3. 重试预算
- 4. 线程池隔离
- 5. 健康检查
- 6. 时钟同步
- 7. 配置验证
- 三十五、数据保留和归档
-
- 1. 业务保留期限
- 2. 归档前验证
- 3. 冷热数据分离
- 4. 删除与隐私请求
- 5. 归档任务幂等
- 三十六、不同业务类型下的变化
-
- 1. 数字商品
- 2. 预约服务
- 3. 票务活动
- 4. 订阅服务
- 5. 多仓履约
- 6. 预售商品
- 三十七、示例故障演练
-
- 演练一:应用在提交后立即退出
- 演练二:支付回调重复十次
- 演练三:消息代理不可用
- 演练四:超时任务重复运行
- 演练五:数据库主库切换
- 三十八、文章中的示例限制
- 三十九、进一步学习和落地清单
-
- 业务梳理
- 设计评审
- 验证准备
- 上线准备
- 四十、最终总结
-
- 面向业务
- 面向前端
- 面向后端
- 面向数据
- 面向架构
- 面向面试
- 四十一、创建订单接口的完整时序
-
- 时序参与者
- 正常时序
- 事务边界解释
- 时序中的不确定点
- 四十二、不同失败点的恢复设计
-
- 在请求到达网关前失败
- 网关认证失败
- 参数校验失败
- 商品不可售
- 价格发生变化
- 库存条件更新失败
- 订单明细插入失败
- 唯一约束冲突
- 事务提交超时
- 响应序列化失败
- Outbox 发布失败
- 四十三、订单查询与状态轮询
-
- 查询接口职责
- 查询响应字段
- 不返回的字段
- 轮询策略
- 长轮询与推送
- 列表与详情一致性
- 四十四、接口字段与校验策略明细
-
- SKU 标识
- 数量
- 幂等键
- 地址或配送信息
- 优惠券标识
- 返回字段
- 四十五、数据库事务示例推演
-
- 正常事务
- 库存不足事务
- 订单写入失败事务
- 幂等冲突事务
- 事务后支付
- 事务后发送消息的风险
- 四十六、库存占用、扣减和释放的语义
-
- 方案 A:下单即扣减
- 方案 B:下单预占,支付后确认
- 方案 C:支付后再扣减
- 方案选择问题
- 四十七、审计与业务时间线
-
- 时间线事件
- 时间线字段
- 审计与普通日志的区别
- 人工操作
- 四十八、重试策略与退避
-
- 可重试错误
- 不应盲目重试的错误
- 指数退避
- 重试责任分配
- 重试可观测性
- 四十九、限流与过载保护
-
- 用户维度
- SKU 维度
- 系统维度
- 限流响应
- 熔断
- 舱壁隔离
- 五十、发布事件的版本化示例
-
- 事件基本结构
- 事件设计原则
- 事件重复
- 事件乱序
- 事件重放
- 五十一、管理后台与客服工具
-
- 订单搜索
- 订单时间线
- 失败原因
- 重试工具
- 数据修复入口
- 隐私保护
- 五十二、SQL 设计审查清单
- 五十三、代码审查清单
- 五十四、业务指标与服务等级目标
-
- 用户体验指标
- 数据正确性指标
- 异步处理指标
- 目标设定
- 五十五、业务边界与法律合规提醒
- 五十六、案例复盘模板
-
- 背景
- 约束
- 问题
- 影响
- 根因
- 处理
- 验证
- 教训
- 五十七、完整场景演练:最后一件商品
-
- 初始数据
- 并发执行
- 结果检查
- 反例:先查后改
- 反例:Redis 先减、数据库失败
- 五十八、完整场景演练:重复提交与响应丢失
-
- 初始条件
- 客户端行为
- 错误重试方式
- 用户修改请求
- 五十九、完整场景演练:支付成功与关闭任务竞争
-
- 事件时间
- 关闭任务执行
- 支付回调执行
- 设计必须回答的问题
- 六十、编码规范与可维护性
-
- 命名
- 方法拆分
- 注释
- 配置
- 六十一、学习顺序建议
-
- 第一步:掌握 HTTP 与浏览器交互
- 第二步:掌握服务端基础
- 第三步:掌握关系型数据库
- 第四步:练习业务状态建模
- 第五步:学习可靠消息
- 第六步:学习生产验证
- 第七步:复盘真实故障
- 六十二、读者自测题
- 六十三、自测题参考答案
- 六十四、博客发布前校对清单
- 六十五、系列化拆分建议
-
- 第一篇:需求与全链路架构
- 第二篇:数据库模型与库存并发
- 第三篇:幂等与请求重试
- 第四篇:订单状态机与超时取消
- 第五篇:支付回调与退款
- 第六篇:Outbox 与消息可靠性
- 第七篇:前端状态和用户体验
- 第八篇:测试、监控与上线
- 六十六、文末交流问题
- 六十七、API 调用示例补充
-
- 查询幂等结果
- 取消订单
- 支付状态查询
- 管理端重放事件
- 六十八、订单状态迁移伪代码
- 六十九、重试和故障矩阵
- 七十、上线前端到端验收脚本
-
- 场景一:正常购买
- 场景二:重复提交
- 场景三:库存耗尽
- 场景四:支付回调重复
- 场景五:订单到期
- 场景六:响应丢失恢复
- 七十一、代码实现的验收标准
- 七十二、最终结论
- 七十三、落地核对:接口到数据库的一致性
-
- 创建接口检查
- 库存写入检查
- 订单持久化检查
- 支付处理检查
- 异步事件检查
- 发布运维检查
- 七十四、结语
- 学习专栏
- 往期文章
摘要: 面试中常见的“如何实现下单”不是只写一个接口。本文以电商商品下单为例,串起前端交互、API 契约、Java 服务、MySQL 库存并发控制、幂等、支付回调和异常恢复,并说明不同规模下如何做方案取舍。
适合读者: Java 后端、前端及全栈开发者 关键词: 全栈面试、订单、库存、幂等、MySQL
一、面试题与业务背景
面试官提问
请设计一个商品下单流程。用户可能重复点击提交,热门商品可能被多人同时购买,服务还会遇到超时、重试和支付回调重复。你会如何保证页面体验、订单正确性和库存不超卖?
业务场景
用户在商品详情页选择规格和数量后提交订单。系统需要校验商品状态和价格、扣减库存、创建待支付订单,并在支付成功后更新订单状态。这里的重点不是某个框架,而是把并发、重试和跨系统失败纳入设计。
下面的代码与容量数字是用于解释方案的示例,不代表特定线上系统数据。先以单体 Java 服务和 MySQL 为基线;缓存、消息队列等组件按业务规模引入。
目标与边界
- 目标:不超卖;同一业务请求重试不重复创建订单;订单与库存变更可追踪、可恢复。
- 边界:本文不展开完整的促销计算、购物车、物流和支付渠道接入。
- 重要约
网硕互联帮助中心



评论前必须登录!
注册