用户连点两下提交,或者网络超时重试,同一个请求发了两次,订单就重复创建了。幂等就是解决这个问题的。
方案一:唯一索引
最简单可靠。给业务字段加唯一索引,重复插入直接报错,捕获后返回已存在。
ALTER TABLE orders ADD UNIQUE KEY uk_order_no (order_no);
适合「创建」类操作,但要求有天然唯一键,比如订单号、支付流水号。
方案二:Token 机制
- 前端打开页面时先请求一个 token
- 提交时带上 token
- 后端用 Redis
SETNX占位,成功才处理,失败说明重复提交 - 处理完删除 token 或设过期
$ok = $redis->set($token, 1, ['nx', 'ex' => 300]);
if (!$ok) {
throw new Exception('请勿重复提交');
}
// 处理业务...
$redis->del($token);
方案三:状态机
比如订单状态从「待支付」到「已支付」,更新时加条件:
UPDATE orders SET status = 'paid' WHERE id = ? AND status = 'pending';
影响行数为 0 说明已经被处理过,直接返回成功。适合状态流转类业务。
方案四:分布式锁
用 Redis 或 ZooKeeper 加锁,锁住业务 key,处理完释放。适合并发高、需要互斥的场景,但要注意锁超时和误删问题。
怎么选
- 有唯一业务键:唯一索引,最省事
- 表单提交防重:Token
- 状态流转:条件更新
- 复杂并发:分布式锁
实际项目里往往是组合用,比如 Token 防前端重复提交 + 唯一索引兜底。

Comments 3 条评论
唯一索引兜底最省事
SETNX 那套防重复提交我在用
状态机条件更新这招挺妙