孤舟笔记 分布式与微服务篇九 什么是幂等性?为什么面试总问它?解决思路一次讲透
文章目录先说结论哪些场景会重复执行方案一唯一 ID 去重表方案二乐观锁版本号方案三Token 机制方案四状态机回答技巧与点评加分回答面试官点评个人网站用户点了一次支付网络超时了又点了一次——会不会扣两次钱这就是幂等性问题。在分布式环境下网络抖动、超时重试、消息重复消费都可能导致同一操作被执行多次。面试官问这题他想听的是你能不能讲清幂等的概念、产生重复的场景、以及具体的解决方案先说结论维度说明定义同一操作执行一次和多次结果完全一致核心问题防止重复提交/重复消费导致数据不一致产生原因网络重试、MQ 重复消费、用户双击解决方案唯一ID 去重表、乐观锁、Token 机制、状态机关键原则天然幂等优先改造幂等其次|一句话记住幂等就像盖章——同一章盖十次纸上还是一个印哪些场景会重复执行场景1用户双击 用户点提交 → 网络慢 → 以为没反应 → 又点一次 → 两次请求 场景2超时重试 支付请求发出 → 网络超时 → 框架自动重试 → 请求到达两次 场景3MQ 重复消费 消费者处理完消息 → 准备确认ACK → 挂了 → MQ重新投递 → 消费两次 场景4微服务重试 服务A调服务B → 超时 → 服务A重试 → 服务B实际已处理 → 处理两次天然幂等的操作不用管天然幂等举例查询SELECT * FROM user WHERE id 1绝对值设置UPDATE user SET name ‘张三’ WHERE id 1删除DELETE FROM temp WHERE id 1非幂等的操作才需要处理非幂等举例新增INSERT INTO order VALUES (…)相对值更新UPDATE account SET balance balance - 100 状态流转UPDATE order SET status ‘PAID’ WHERE status ‘UNPAID’方案一唯一 ID 去重表每个请求带一个唯一 ID处理前先查去重表// 幂等控制publicResultprocessOrder(OrderRequestreq){StringreqIdreq.getRequestId();// 客户端生成的唯一ID// 1. 查去重表是否已处理if(dedupMapper.exists(reqId)){returnResult.success(已处理);// 直接返回不重复执行}// 2. 插入去重表利用唯一索引保证原子性dedupMapper.insert(reqId);// 3. 执行业务逻辑orderService.createOrder(req);returnResult.success();}去重表设计idempotent_log (request_id PK, result, created_at)就像医院的挂号系统——同一个挂号号只能看一次病来了第二次直接告诉你已经看过了。方案二乐观锁版本号更新数据时带上版本号版本号不对就拒绝// 扣款——乐观锁UPDATEaccountSETbalancebalance-100,versionversion1WHEREid1ANDversion5;// 版本号必须匹配// 影响行数 0 → 说明已被其他人/重试修改过不重复扣款乐观锁适合更新场景保证同一版本只更新一次。方案三Token 机制服务端先发 Token提交时带上 Token服务端验证并删除1. 进入页面 → GET /api/token → 服务端生成 Token 存 Redis 2. 提交表单 → POST /api/order?tokenxxx → 服务端删除 Token 3. 重复提交 → POST /api/order?tokenxxx → Token 已不存在 → 拒绝 // 提交时校验 TokenBooleandeletedredis.delete(token);// 原子操作删除成功才处理if(!deleted){returnResult.fail(请勿重复提交);}// 执行业务逻辑就像电影票——一张票只能检一次撕了就不能再用。方案四状态机业务本身有状态流转利用状态约束保证幂等// 支付——只有待支付才能变已支付introwsorderMapper.updateStatus(orderId,UNPAID,PAID);// UPDATE order SET statusPAID WHERE id? AND statusUNPAID if(rows0){// 状态已经不是UNPAID可能已支付直接返回成功returnResult.success(已支付);}幂等性全景 重复原因 ├── 用户双击 ├── 网络重试 ├── MQ重复消费 └── 微服务重试 天然幂等 ├── 查询、绝对值设置、删除 └── 不需要额外处理 解决方案 ├── 唯一ID去重表 → 通用适合新增场景 ├── 乐观锁 → 适合更新场景 ├── Token机制 → 适合前端防重复提交 └── 状态机 → 适合有状态流转的业务 口诀同一操作多次行结果一致叫幂等 去重表查唯一ID乐观锁靠版本号 Token一次就作废状态机控流转方向 天然幂等不用管非幂等才要防范回答技巧与点评标准回答幂等性指同一操作执行一次和多次结果一致。分布式环境下网络重试、MQ 重复消费、用户双击都可能导致重复执行。解决方案1唯一 ID 去重表——请求带唯一 ID处理前查表判断2乐观锁——更新时校验版本号3Token 机制——服务端发 Token提交时验证并删除4状态机——利用业务状态约束防止重复流转。原则天然幂等的操作无需处理非幂等操作根据场景选方案。加分回答去重表要和业务表在同一个事务中否则去重记录插入了但业务失败了下次请求被拦截就永远无法处理。正确做法是去重记录和业务操作在同一个数据库事务中MQ 消费幂等RocketMQ 和 Kafka 都可能重复投递消息。最佳实践是消费端用消息 ID 去重表保证幂等而不是依赖 MQ 的 exactly-once 语义HTTP 幂等性规范GET/PUT/DELETE 天然幂等POST 非幂等。RESTful 设计中创建资源用 POST非幂等更新用 PUT幂等这是一个重要的设计原则面试官点评这道题考的是你对分布式可靠性的理解。最忌讳的回答是只知道防止重复提交——面试官想听的是具体场景和解决方案的对应关系扣款用什么方案订单状态流转用什么方案能根据场景选方案再讲清实现细节就是高分回答。原文阅读内容有帮助点赞、收藏、关注三连评论区等你