幂等性设计艺术:在分布式重试风暴中构筑坚不可摧的防线

幂等性设计艺术:在分布式重试风暴中构筑坚不可摧的防线

​2023年某支付平台凌晨故障​​:

由于网络抖动导致支付指令重复发送,系统在2分钟内处理了​​17万笔重复交易​​,引发​​4.2亿资金风险​​。

事故根本原因:​​缺少幂等防护​​的支付接口在重试机制下成为"资金黑洞"。

一、幂等性:分布式系统的生命线

1.1 什么是幂等性?

​数学定义​​:

对于操作f,若满足 f(f(x)) = f(x),则称f具有幂等性

​分布式系统定义​​:

一个操作无论被执行一次还是多次,对系统状态的影响都是相同的

1.2 为什么需要幂等性?

​分布式环境四大不确定性​​:

  1. 网络超时重试

  2. 消息队列重复投递

  3. 客户端重复提交

  4. 故障恢复后补偿

二、幂等性实现模式全景图

2.1 唯一请求ID模式(全局ID方案)

实现原理:

Java实现:
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;public class IdempotentService {// 使用分布式缓存如Redis生产环境private final ConcurrentMap<String, Object> requestCache = new ConcurrentHashMap<>();public Response processRequest(Request request) {String requestId = request.getRequestId();// 检查是否已处理Object cachedResult = requestCache.get(requestId);if (cachedResult != null) {return (Response) cachedResult;}// 获取分布式锁(防并发重复)Lock lock = distributeLock.lock(requestId);try {// 双重检查cachedResult = requestCache.get(requestId);if (cachedResult != null) {return (Response) cachedResult;}// 执行业务逻辑Response response = executeBusiness(request);// 记录结果(设置合理过期时间)requestCache.put(requestId, response, 24, TimeUnit.HOURS);return response;} finally {lock.unlock();}}// 业务执行示例private Response executeBusiness(Request request) {// 核心业务逻辑Payment payment = paymentService.create(request);return new Response(200, "支付成功", payment);}
}

​适用场景​​:

  • 支付交易

  • 订单创建

  • 重要业务操作

2.2 状态机模式(业务状态约束)

状态流转图:

Java实现(乐观锁方案):
public class OrderService {@Transactionalpublic void payOrder(String orderId, BigDecimal amount) {Order order = orderDao.findById(orderId);// 状态检查if (order.getStatus() != OrderStatus.PENDING) {throw new IllegalStateException("订单状态异常");}// 乐观锁更新int rows = orderDao.updateStatus(orderId, OrderStatus.PENDING, // 旧状态OrderStatus.PAID     // 新状态);if (rows == 0) {// 更新失败,可能已被其他请求处理throw new ConcurrentUpdateException();}// 扣减库存等后续操作inventoryService.reduce(order.getProductId(), order.getQuantity());}
}

​适用场景​​:

  • 订单状态变更

  • 工作流引擎

  • 库存管理

2.3 令牌桶模式(预取号机制)

工作流程:

Java实现:
public class TokenService {// 使用Redis存储令牌状态private final RedisTemplate<String, Boolean> redisTemplate;// 生成令牌public String generateToken(String businessType) {String token = UUID.randomUUID().toString();String key = "token:" + businessType + ":" + token;// 设置过期时间30分钟redisTemplate.opsForValue().set(key, false, 30, TimeUnit.MINUTES);return token;}// 验证并消耗令牌public boolean consumeToken(String businessType, String token) {String key = "token:" + businessType + ":" + token;// 使用Lua脚本保证原子性String script = "if redis.call('get', KEYS[1]) == false then " +"   redis.call('set', KEYS[1], true) " +"   return true " +"else " +"   return false " +"end";return redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class),Collections.singletonList(key));}
}// 客户端使用
public class PaymentController {@PostMapping("/pay")public Response pay(@RequestBody PaymentRequest request) {// 验证令牌if (!tokenService.consumeToken("payment", request.getToken())) {return new Response(400, "重复请求");}// 处理支付return paymentService.process(request);}
}

​适用场景​​:

  • 防止表单重复提交

  • 短信验证码校验

  • 敏感操作确认

三、HTTP幂等性深度解析

3.1 HTTP方法幂等性矩阵

方法

是否幂等

原因说明

GET

只读操作,不影响资源状态

HEAD

同GET,不返回响应体

PUT

全量替换资源

DELETE

删除资源,多次删除结果相同

POST

​否​

每次创建新资源

PATCH

通常否

部分更新可能产生不同结果

OPTIONS

获取服务器支持的方法

3.2 POST方法实现幂等的三种方案

四、行业级应用实践

4.1 消息队列幂等消费(Kafka实现)

public class KafkaConsumerService {private final Map<TopicPartition, Set<Long>> processedOffsets = new ConcurrentHashMap<>();@KafkaListener(topics = "payment")public void handlePayment(ConsumerRecord<String, PaymentMessage> record) {TopicPartition tp = new TopicPartition(record.topic(), record.partition());long offset = record.offset();// 检查是否已处理if (processedOffsets.computeIfAbsent(tp, k -> ConcurrentHashMap.newKeySet()).contains(offset)) {return; // 已处理,跳过}try {paymentService.process(record.value());// 记录已处理offsetprocessedOffsets.get(tp).add(offset);} catch (Exception e) {// 处理失败,不记录offsetthrow e;}}// 定期清理旧offset@Scheduled(fixedRate = 60000)public void cleanProcessedOffsets() {long now = System.currentTimeMillis();processedOffsets.forEach((tp, offsets) -> {offsets.removeIf(offset -> offset < getOldestUnprocessedOffset(tp));});}
}

4.2 分布式库存扣减(Redis+Lua)

-- KEYS[1]: 库存key
-- ARGV[1]: 扣减数量
-- ARGV[2]: 请求IDlocal key = KEYS[1]
local quantity = tonumber(ARGV[1])
local requestId = ARGV[2]-- 检查请求是否已处理
if redis.call('sismember', key..':processed', requestId) == 1 thenreturn 0 -- 已处理
end-- 检查库存
local stock = tonumber(redis.call('get', key))
if stock < quantity thenreturn -1 -- 库存不足
end-- 扣减库存
redis.call('decrby', key, quantity)
redis.call('sadd', key..':processed', requestId)return 1 -- 成功

4.3 支付系统幂等设计

五、避坑指南:幂等设计的致命陷阱

5.1 经典反模式案例

​案例1:订单重复创建​

// 错误实现:缺少幂等检查
public Order createOrder(OrderRequest request) {// 直接创建订单Order order = new Order(request);return orderRepository.save(order);
}

​案例2:数据库幂等失效​

/* 危险操作:非幂等更新 */
UPDATE account SET balance = balance - 100 WHERE user_id = 123;
-- 重试时重复扣款

5.2 幂等设计十大黄金法则

  1. ✅ ​​前置检查​​:在执行业务前进行幂等验证

  2. ✅ ​​状态约束​​:利用业务状态机防止重复流转

  3. ✅ ​​请求标识​​:全局唯一ID贯穿整个请求链路

  4. ✅ ​​原子操作​​:使用数据库事务或Lua脚本保证原子性

  5. ✅ ​​过期机制​​:为幂等记录设置合理过期时间

  6. ✅ ​​错误隔离​​:区分幂等错误和业务错误

  7. ✅ ​​版本控制​​:业务变更时考虑幂等兼容性

  8. ✅ ​​压力测试​​:在高并发下验证幂等设计

  9. ✅ ​​监控告警​​:对重复请求进行监控

  10. ✅ ​​文档规范​​:明确接口幂等特性

六、进阶:分布式环境下的挑战与解决方案

6.1 分库分表下的幂等挑战

​解决方案​​:

6.2 跨系统幂等传递

​Saga事务中的幂等设计​​:

public class OrderSaga {@SagaSteppublic void reserveInventory(Order order) {// 幂等键:订单ID+步骤名String idempotentKey = order.getId() + ":reserveInventory";if (idempotencyService.isProcessed(idempotentKey)) {return;}inventoryService.reserve(order.getItems());idempotencyService.markProcessed(idempotentKey);}@Compensatepublic void compensateReserve(Order order) {// 补偿操作同样需要幂等String idempotentKey = order.getId() + ":compensateReserve";if (idempotencyService.isProcessed(idempotentKey)) {return;}inventoryService.cancelReservation(order.getItems());idempotencyService.markProcessed(idempotentKey);}
}

七、思考题

  1. ​设计题​​:

    如何设计一个支持百亿级请求的去重系统?要求:

    • 99.99%的精确去重

    • 存储成本低于1TB

    • 毫秒级响应时间

      请描述架构和核心算法选择

  2. ​故障分析​​:

    某系统虽然实现了幂等设计,但在数据库主从切换后出现重复处理,可能的原因是什么?如何解决?

  3. ​性能优化​​:

    在高并发场景下(10万QPS),幂等检查成为性能瓶颈,有哪些优化方案?


​分布式系统设计箴言​​:

"在分布式世界中,任何可能出错的事情终将出错。

幂等性不是可选项,而是系统稳定性的最后一道防线。"

—— 分布式系统设计原则

​性能对比​​:

方案

吞吐量(QPS)

存储开销

适用场景

数据库唯一索引

2,500

低频关键业务

Redis去重

45,000

高频业务

布隆过滤器

120,000+

可容忍误判场景

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/95215.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/95215.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零开始理解NDT算法的原理及应用

1. 概述 NDT&#xff0c;全称 Normal Distributions Transform&#xff08;正态分布变换&#xff09;&#xff0c;是一种广泛使用的点云配准算法&#xff0c;它的核心思想与ICP截然不同&#xff1a;NDT不直接计算点与点之间的对应关系&#xff0c;而是通过概率模型来描述和匹配…

艾体宝案例 | 数据驱动破局:DOMO 如何重塑宠物零售门店的生存法则

某连锁宠物店店长紧盯着电脑屏幕上的Excel表格&#xff0c;手指机械地在键盘上敲击出“CtrlC/V”的组合键——这是她第17次尝试将三个不同系统的数据拼凑到一起。门店POS机导出的销售记录、会员系统的消费偏好、库存管理的临期预警&#xff0c;这些本应串联起门店运营全貌的关键…

极飞科技AI智慧农业实践:3000亩棉田2人管理+产量提15%,精准灌溉与老农操作门槛引讨论

在新疆尉犁县的棉田里&#xff0c;两架农业无人机正沿着设定航线低空飞行&#xff0c;它们掠过之处&#xff0c;传感器实时传回土壤湿度和作物长势数据——这片3000亩的棉田&#xff0c;如今只需要两名90后新农人通过手机管理&#xff0c;产量却比传统种植模式提高了15%。这不是…

企业级-搭建CICD(持续集成持续交付)实验手册

搭建CI/CD(持续集成/持续交付)企业示例 为了让容器构建镜像并可以持续集成&#xff0c;可以自动上传到Harbor仓库&#xff1b;并且业务主机可以通过CD自动从仓库中下载镜像latest版本并实现业务更新。1.环境部署 1.1 环境搭建业务IP域名GitLab172.25.254.50gitlab.dhj.orgJenki…

吃透《数据结构》C 语言版:线性表的类型定义详解

作为数据结构的入门章节&#xff0c;线性表就像 “地基” 一样重要&#xff0c;而第二章 2.3 节的 “线性表的类型定义”&#xff0c;更是理解后续操作&#xff08;插入、删除、查找等&#xff09;的核心前提。今天就结合自己的学习笔记&#xff0c;用通俗的语言拆解这个知识点…

文件系统中的核心数据结构

宏观上文件系统在kernel的形态文件系统运作流程按照:vfs->磁盘缓存->实际磁盘文件系统->通用块设备层->io调度层->块设备驱动层->磁盘。具体流程的详细展现如下如如何理解文件系统中的数据结构&#xff1f;linux中文件系统还有几种核心数据结构分别是super_b…

TDengine与StarRocks在技术架构和适用场景上有哪些主要区别?

TDengine 与 StarRocks 作为国产数据库领域的代表性产品&#xff0c;分别专注于时序数据处理和高性能分析场景&#xff0c;在技术架构和适用场景上存在显著差异。以下从核心架构、数据模型、性能特点及典型应用场景等方面进行对比分析&#xff1a;&#x1f3d7;️ ​​一、技术…

Qt事件_xiaozuo

Qt事件Qt 的事件机制是其实现用户交互和系统响应的核心框架&#xff0c;基于事件驱动模型构建。以下从五个关键方面详细解释其工作原理和用法&#xff1a;1. 事件&#xff08;QEvent&#xff09;的定义与分类事件本质&#xff1a;事件是 QEvent 类或其子类的实例&#xff0c;用…

运动控制技术:自动化与智能驱动的核心

一、运动控制概述运动控制技术是自动化技术和电气拖动技术的融合&#xff0c;以工控机、PLC、DSP等为控制器的运动控制技术融合了微电子技术、计算机技术、检测技术、自动化技术以及伺服控制技术等学科的新成果&#xff0c;在工业生产中起着极为重要的作用。早期的运动控制技术…

链表实战指南:手动实现单链表与双链表的接口及OJ挑战(含完整源码)

文章目录一、链表的概念二、链表的分类三、手动实现单链表1.链表的初始化2.链表的打印3.申请新的节点大小空间4.链表的尾插5.链表的头插6.链表的尾删7.链表的头删8.链表的查找9.在指定位置之前插入数据10.在指定位置之后插入数据11.删除指定节点12.删除指定节点之后的数据13.销…

Spring 事件驱动编程初探:用 @EventListener 轻松处理业务通知

一、核心概念与模型Spring 的事件机制是观察者模式&#xff08;也叫发布-订阅模型&#xff09;的一种典型实现。它主要由三个核心部分组成&#xff1a;事件 (Event)&#xff1a; 承载信息的对象&#xff0c;通常是某种状态变化的通知。可以是继承 ApplicationEvent 的类&#x…

无人机也能称重?电力巡检称重传感器安装与使用指南

在无人机电力巡检中&#xff0c;工程师们常常面临一个棘手难题&#xff1a;如何精确知道新架设或老旧缆线的实际负重&#xff1f; 传统依靠老师傅“肉眼估算”的方法不仅风险极高&#xff0c;而且数据极不准确&#xff0c;给电网安全埋下巨大隐患。难道没有更科学的方法吗&…

第二阶段WinForm-8:特性和反射,加密和解密,单例模式

1_预处理指令 &#xff08;1&#xff09;源代码指定了程序的定义&#xff0c;预处理指令&#xff08;preprocessor directive&#xff09;指示编译器如何处理源代码。例如&#xff0c;在某些情况下&#xff0c;我们希望编译器能够忽略一部分代码&#xff0c;而在其他情况下&am…

基于mac的智能语音处理与应用开发-环境部署

上一次写文章还是上一次&#xff0c;时隔一年再次开启学习之路。新机mac没有开发环境&#xff0c;在gpt老师的指导下开始学习之路。 mac开发环境的部署参考了b站程序员云谦和Clover-You的视频教程&#xff0c;然后结合自身及gpt老师的帮助现在开始部署。 g老师的&#x1f34e…

Java中使用正则表达式的正确打开方式

正则表达式基础语法Java正则表达式基于java.util.regex包&#xff0c;核心类是Pattern和Matcher。基本语法遵循标准正则规范&#xff1a;. 匹配任意单个字符&#xff08;除换行符&#xff09;\d 匹配数字&#xff0c;等价于 [0-9]\w 匹配单词字符&#xff0c;等价于 [a-zA-Z0-9…

Docker中Mysql容器忽略大小写

场景说明 在数据迁移场景中&#xff0c;从一个数据库中将数据迁移到另一个数据&#xff0c;经常会遇到&#xff0c;两个不同数据库之间&#xff0c;一个默认忽略大小写&#xff0c;一个默认不忽略大小写&#xff0c;导致实际业务层服务进行数据库访问时&#xff0c;切换数据库之…

神经网络激活函数:从ReLU到前沿SwiGLU

摘要 本文全面介绍了神经网络中常用的激活函数,包括Sigmoid、Tanh、ReLU等传统函数,以及2017年后出现的Swish、Mish、SwiGLU等新兴函数。每个函数均提供数学定义、优缺点分析、Python实现代码和可视化图像,并附有实际应用建议和性能对比数据,帮助读者根据具体任务选择合适…

线程池常见面试问答

好嘞 &#x1f44d;&#xff0c;我帮你把这些 线程池 并发编程八股文 整理成 问答对照表&#xff08;Q & A&#xff09;&#xff0c;你面试时可以直接用。&#x1f9fe; 线程池常见面试问答一、基础语法 & STLQ1&#xff1a;std::function<void()> 和函数指针的…

Flutter 开发技巧 AI 快速构建 json_annotation model 的提示词

将下面这段复制到AI GPT、DeepSeek 、文心快码 试过效果都可以&#xff0c;不用做任何更改。将 json 数据丢给 AI 就行了 我会提供一段 JSON 数据&#xff0c;请帮我生成 Dart 模型&#xff0c;要求严格如下&#xff1a;1. 使用 json_annotation 包&#xff0c;包含&#xff1a…

【秋招笔试】2025.08.30科大讯飞秋招笔试题

📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 👉 笔试突围在线刷题 bishipass.com 科大讯飞 题目一:物品种类统计 1️⃣:使用集合或哈希表统计不同物品编号的数量 2️⃣:利用数学公式 n - 不同种类数 计算最终答案 难度:简单 这道题目的关…