当你疯狂点击“购买”按钮,却发现自己下了 5 个相同订单;或者因为服务器延迟,你的支付重复进行了好几次…… 是不是一不小心就可能亏大了?别怕,咱们今天就来聊聊如何用幂等性策略,让你在分布式系统或高并发场景下,稳稳地“只执行一次”!
幂等性(Idempotency) 是指一个操作无论执行多少次,产生的结果都是相同的,即多次执行不会对系统状态造成额外影响。
数学定义:幂等操作满足: f(f(x))=f(x),即,无论 f(x)
被调用多少次,结果都不变。
举个生活中的例子:
在编程世界也是同理:无论是后端接口、数据库操作还是消息处理,一旦你设计好 幂等,就不用担心重复执行带来的尴尬场面。
来看看如果没有幂等,可能会发生什么糟心事:
结论:没有幂等,系统就像个“爱吃回头草”的“小马”,老是踩到同一个坑里。为了让它少撞墙,需要在关键环节加上幂等保护。
核心思路:利用数据库的唯一索引或主键,来保证数据只能插入一次。
场景示例:订单号
在创建订单时,设置 订单号 (orderId) 为唯一主键。
CREATE TABLE orders (
order_id VARCHAR(50) PRIMARY KEY,
user_id INT,
amount DECIMAL(10,2)
);
适用场景:
核心思路:客户端先请求一个唯一 Token(就像一张唯一门票),随后在操作请求时带上这张门票,服务端每看到一张门票就“作废”它,拒绝使用第二次。
具体步骤:
- 客户端先请求 Token
- 客户端带 Token 调用正式接口
- 服务端检查并“消耗”该 Token
3.1 通过 Redis 的
setIfAbsent(token, "used")
来判断此 Token 是否已被用过3.2 如果已被用过,说明请求重复了,直接拦截
适用场景: 表单重复提交、支付重复操作等短时间内易发生重复的业务请求。
核心思路:
String requestId = request.getHeader("Idempotency-Key");
if (redis.exists(requestId)) {
// 重复请求,直接返回之前的处理结果
return redis.get(requestId);
} else {
// 首次处理,逻辑执行完后,存储 result 到 redis
redis.set(requestId, result, 10, TimeUnit.MINUTES);
}
适用场景:对外提供的 API,需要保证相同请求只处理一次。比如第三方支付系统常用这个方法,API 保证幂等。
在消息队列 (Kafka、RabbitMQ、RocketMQ) 中,消息重复投递是一种常见现象。消费者需要自行去重:
String messageId = msg.getMessageId();
if (redis.exists(messageId)) {
// 该消息已消费过,直接丢弃
return;
}
processMessage(msg); // 执行业务逻辑
redis.set(messageId, "done", 10, TimeUnit.MINUTES);
适用场景:
当多个线程或微服务都要操作同一份资源(比如扣减库存),我们可以使用分布式锁来避免在执行前就发生重叠操作。
boolean lock = redis.setIfAbsent("lock:inventory:123", "1", 5, TimeUnit.SECONDS);
if (!lock) {
throw new RuntimeException("有其他进程正在处理,请稍后重试!");
}
// 如果获取到了锁,执行业务逻辑
updateInventory("123", 10);
redis.delete("lock:inventory:123");
适用场景: 高并发的场景,比如 抢购秒杀、秒杀订单,防止库存被瞬间减多次。
比如订单服务 A -> 库存服务 B -> 支付服务 C,如果 A 重试了两次,就有可能让 B、C 也执行两次。必须在每个服务之间都带上一个幂等标识,哪怕调用链路中途失败,也能保障只执行一次。
使用消息队列时,尤其要做好 消息幂等消费,否则就会出现再发一次消息就会再次扣钱或者再次更新库存的严重问题。
在做幂等判定时,如果需要同时更新多张表,要保证它们要么同时成功,要么同时失败。可以借助事务或分布式事务框架(比如 Seata)去解决。
幂等性就像给系统装了一把“时光机”:它让我们在面对重复请求、重复消息时,能回到正确的状态,避免二次伤害。
只要设计得好,重复请求不再是大魔王!你的用户和老板会爱上这种“只扣一次费、只生成一次订单、只更新一次状态”的安全感!
祝各位在幂等之路上一路披荆斩棘,构建更可靠、更稳定的系统。下次见啦!
如果觉得这篇分享对你有帮助,欢迎点赞、留言或者分享给你的同事和朋友~