Skip to content

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),锁内先查再插,串行化同一用户的重复请求。

④ 状态机校验 ​

业务层判断:如果该用户已存在"待支付"订单,提示去支付而不是再建一单(电商常见策略)。

补充:防止"重复支付" ​

  • 支付回调处理要幂等:同一笔支付通知多次到达,只处理一次(支付流水唯一约束);
  • 对账系统兜底发现异常单。

一句话总结 ​

前端防手滑、后端幂等键 + 数据库唯一约束 + 分布式锁三重防御;核心是"同一业务请求只生效一次",靠唯一键兜底保证最终一致性。

基于 VitePress 重建