接口幂等性设计:从理论到落地的完整方案
API Idempotency Design: A Complete Solution from Theory to Implementation
| Kevin | 2026-08-29T16:41:32
接口幂等性是分布式系统绑不开的话题。这篇文章分享一下我们在实际项目中用过的几种幂等方案和它们的适用场景。
API idempotency is essential in distributed systems. Sharing practical idempotency patterns and their use cases from real projects.
接口幂等性这个话题老生常谈了,但真正在项目中做好并不容易。分享一下我们用过的几种方案。 什么场景需要幂等 用户快速双击提交按钮 网络超时后前端自动重试 消息队列重复投递 分布式事务补偿重试 方案一:Token 机制 最通用的方案。流程: 前端先调一个接口获取 token 提交请求时带上这个 token 后端在 Redis 里用 SETNX 检查 token 是否已使用 如果 token 已存在,说明是重复请求,直接返回之前的结果 public Result<?> createOrder(@RequestHeader("X-Idempotency-Token") String token, @RequestBody OrderDTO dto) { String key = "idempotent:" + token; Boolean acquired = redis.opsForValue().setIfAbsent(key, "processing", 30, TimeUnit.MINUTES); if (!acquired) { String cachedResult = redis.opsForValue().get(key); return JSON.parseObject(cachedResult, Result.class); } try { Result<?> result = orderService.create(dto); redis.opsForValue().set(key, JSON.toJSONString(result), 30, TimeUnit.MINUTES); return result; } catch (Exception e) { redis.delete(key); throw e; } } 方案二:唯一约束 利用数据库的唯一索引来防重。适合有天然唯一标识的场景: -- 订单号 + 操作类型 唯一 ALTER TABLE order_operations ADD UNIQUE INDEX uk_order_op (order_no, op_type); -- 插入时用 INSERT IGNORE 或 ON DUPLICATE KEY INSERT IGNORE INTO order_operations (order_no, op_type, ...) VALUES (?, ?, ...); 方案三:状态机 对于有状态流转的业务,利用状态机来保证幂等: -- 只有"待支付"状态才能变成"已支付" UPDATE orders SET status = 'paid', paid_at = NOW() WHERE order_no = ? AND status = 'pending'; -- affected rows = 0 说明状态已经变了,是重复请求 选择建议 通用场景(创建类接口)→ Token 机制 有天然唯一键的场景 → 唯一约束 状态流转类接口 → 状态机 消息消费 → 消息 ID 去重表
Idempotency is talked about a lot but hard to implement well. Here are three practical patterns. Pattern 1: Token Mechanism Frontend gets a token, includes it in requests. Backend uses Redis SETNX to detect duplicates and cache results. Pattern 2: Unique Constraints Use database unique indexes for natural deduplication with INSERT IGNORE. Pattern 3: State Machine Leverage state transitions - only allow valid transitions, reject duplicates based on current state.