题目:如何解决接口幂等的问题?
难度:medium
标签:arch/distributed/idempotent, architecture/idempotent, difficulty/medium
评分:⭐⭐⭐☆☆(3/5)
首先确定一下接口幂等的概念。接口幂等本质上就说防止用户反复的提交相同的请求,哪怕是误触也好,是刷机也好,要处理类似的这种请求,误触、重试都有可能产生幂等性的问题,我们要处理的就是这种问题。
然后知道了要处理的问题是什么,然后就开始考虑怎么去实现。首先是要识别这个请求是重复的,一个是通过时间,连续的、短暂的连续时间内同样的请求,然后是请求内容,同样的请求去识别出你这个请求是重复的,而不是说常规的、正常的请求,识别出是重复请求之后,再进行对应的处理。
业务处理上,常规要么就是把前置的处理掉,然后直接快速返回,你已经重复提交过了,或者是返回,直接返回一个正常的请求结果,因为前面已经有提交的了。
这一套下来,基本上就一个常规的幂等接口幂等的方案。
另外常见的一些处理幂等的方式,比如说像订单的状态机,就是一个处理幂等的方式。一个订单只能从一个状态往下一个状态走,你继续用上一个状态的请求过来,是达不到正常的结果的。
另外就是还有,比如说像查询,它自身本身就是幂等的,并不需要做什么幂等性的处理,只需要做频度的限制。
而对于前置刚刚说的重复数据的识别,一般使用的方案是对请求的入参进行哈希处理,然后判定当前的请求是否连续请求过,然后另外加缓存,缓存去判定这个请求是否短时间内重复的请求了。如果判定是的,就判定它是一个不合理的请求,然后这时候给它进行打回,或者是给它一个已经处理完毕,或者你重复请求了的一个异常反馈。这就是我对幂等问题处理的方案。
优点:
不足:
SET key value NX EX 300 一次搞定"检查+写入"补充:
SET NX EX + Lua 脚本原子操作;DB INSERT IGNORE 或 ON DUPLICATE KEY核心考点定位:考察对幂等的多层次理解——接口层、业务层、存储层各自的实现方式与取舍。
知识点拆解:
UNIQUE KEY (order_no, biz_type) 防止脏数据INSERT IGNORE / ON DUPLICATE KEY UPDATE:MySQL 原生支持递进:定义 → 天然幂等/非幂等 → 三层方案 → 分布式坑 → 综合实践
考察方向:
延伸递进面试题:
pay_request_no(幂等 key),全链路带此 key面试官您好,接口幂等的本质是"多次相同请求产生的效果和一次请求相同"。像 GET、PUT 天然幂等,POST 通常非幂等需要额外设计。我的做法是分层建设三层防线。
第一层是接口层幂等,用 Token 模式或 Idempotency-Key 请求头。Token 模式是客户端先向 /token 接口申请一次性凭证,服务端存 Redis 并设置过期时间;业务请求携带 token,服务端用 SET NX EX 原子操作消费 token,消费成功才继续处理业务。这样重复请求因为 token 已被消费而被直接拦截,不会重复执行。对于支付等关键场景,可以强制要求客户端生成 pay_request_no 作为幂等 key,全链路带此 key。
第二层是业务层幂等,通过状态机约束。比如订单只能从"待支付"推进到"已支付",重复的支付请求因当前状态已经不是"待支付"而被业务逻辑拒绝,返回业务错误码但不会二次扣款。业务唯一键也是这一层的关键,像订单号、支付流水号这些业务侧的自然唯一标识,可以贯穿全链路。
第三层是存储层幂等,用数据库唯一索引兜底。即使前两层都失效了,比如 Redis 宕机或业务代码有 bug,DB 唯一索引也能阻止脏数据写入。比如订单表加 UNIQUE KEY (order_no, pay_channel),重复支付请求会因唯一约束冲突被拒绝。工程实践上常用 INSERT IGNORE 或 ON DUPLICATE KEY UPDATE 让唯一冲突变成软失败。
分布式下有个常见坑:Redis 与 DB 不共享事务,光有 Redis SETNX 不够,必须 DB 唯一索引兜底。另外区分一下:幂等是业务语义层要求(多次请求效果一致),防重是接口层要求(拒绝短时间重复),限流是流量层要求(控制并发)。三者互补而不是替代。
在实际工程里,一次完整的支付幂等设计会同时用到 Token + 业务唯一键 + 状态机 + 唯一索引四层,任何一层都能兜住不同失败模式。这也体现了幂等不是一个点,而是一套纵深防御体系。