P07 如何防止重复下单
面试题:用户连点两次"提交订单",或网络超时重试,导致产生多笔重复订单,怎么防止?
分层防御:前端拦截 + 后端幂等
1. 前端拦截(治标)
- 提交后按钮置灰/禁用,加 loading;
- 提交后跳转订单页,防止返回重提;
- 视频原话:这种方式只能防君子、不能防小人,必须靠后端兜底。
2. 后端幂等(治本)
① 幂等键 / 请求唯一标识(推荐)
下单前先向后端获取一个幂等键(token/requestId),提交订单时必须带上:
text
用户点"提交" → 先调接口获取 token → 下单请求携带 token
服务端:token 不存在则拒绝;存在则消费(删除/标记已用)后处理- 用 Redis
SETNX记录幂等键:SETNX只有 key 不存在时才保存成功并返回 true,重复请求返回 false 直接拦截; - 视频里的 key 设计:用户唯一标识(登录 token)+ 当前商品 ID 组合,保证"同一用户同一商品"只能下一单;
- 幂等 key 要设过期时间(如 3~5 秒):超短窗口内重复点击都拦掉,过期后允许再次正常下单;
- 已处理过的请求返回上一次结果(幂等)。
② 数据库唯一约束兜底
订单表加唯一键,如 user_id + biz_id(业务单号)或幂等键字段:
sql
ALTER TABLE t_order ADD UNIQUE KEY uk_biz (user_id, biz_id);重复插入时数据库抛唯一键冲突,捕获后按"已存在订单"处理,这是最可靠的兜底。
③ 分布式锁
对"用户+下单维度"加分布式锁(Redis setnx/Redisson),锁内先查再插,串行化同一用户的重复请求。
④ 状态机校验
业务层判断:如果该用户已存在"待支付"订单,提示去支付而不是再建一单(电商常见策略)。
补充:防止"重复支付"
- 支付回调处理要幂等:同一笔支付通知多次到达,只处理一次(支付流水唯一约束);
- 对账系统兜底发现异常单。
一句话总结
前端防手滑、后端幂等键 + 数据库唯一约束 + 分布式锁三重防御;核心是"同一业务请求只生效一次",靠唯一键兜底保证最终一致性。