什么是幂等性?
幂等性(Idempotence)是数学和计算机科学中的一个重要概念,指对同一个系统使用相同参数的一次或多次调用所产生的影响与一次调用的影响相同。换句话说,无论操作执行一次还是多次,结果都是相同的。
幂等性的重要性
在分布式系统、网络通信和API设计中,幂等性至关重要,因为:
- 网络请求可能会超时重试
- 客户端可能会重复提交
- 消息可能会被重复消费
常见的幂等性问题场景
- 用户重复点击提交按钮
- 接口超时后的重试机制
- 消息队列的重复消费
- 支付系统的重复支付请求
幂等性解决方案
1. 唯一标识符(Token)机制
实现方式:
- 服务端生成唯一token返回给客户端
- 客户端携带token发起请求
- 服务端验证token后执行操作并删除/标记token
// 生成token示例
String token = UUID.randomUUID().toString();
cache.put(token, "valid", 5, TimeUnit.MINUTES);
// 验证token示例
if (cache.get(token) != null) {
cache.remove(token);
// 执行业务逻辑
} else {
// 拒绝重复请求
}
2. 乐观锁机制
适用于更新操作,通过版本号控制:
UPDATE orders SET status = 'paid', version = version + 1
WHERE order_id = 1001 AND version = 2;
3. 唯一约束
利用数据库唯一索引防止重复数据:
CREATE TABLE payments (
id BIGINT PRIMARY KEY,
order_id VARCHAR(32) UNIQUE,
amount DECIMAL(10,2),
-- 其他字段
);
4. 状态机控制
确保业务状态只能按预定流程转变:
if (order.getStatus().equals("unpaid")) {
order.setStatus("paid");
// 处理支付逻辑
} else {
// 已处理过,直接返回成功
}
5. 去重表
创建专门记录请求ID的表:
CREATE TABLE request_ids (
request_id VARCHAR(64) PRIMARY KEY,
created_at TIMESTAMP
);
6. 分布式锁
使用Redis或Zookeeper实现分布式锁:
// 使用Redis实现分布式锁
boolean locked = redis.setnx("lock:order:1001", "1");
if (locked) {
try {
// 执行业务逻辑
} finally {
redis.del("lock:order:1001");
}
}
不同HTTP方法的幂等性设计
方法 | 幂等性 |
GET | 是 |
HEAD | 是 |
PUT | 是 |
DELETE | 是 |
POST | 否 |
PATCH | 通常否 |
实际应用建议
- 读操作:天然幂等,无需特别处理
- 写操作:需要根据业务场景选择合适的幂等方案
- 支付类系统:建议结合Token机制和唯一约束
- 高并发场景:优先考虑乐观锁或分布式锁