面试知识库
极高 进阶

幂等性设计#

一句话答案#

同一操作执行多次结果一致,方案:唯一请求 ID + 去重表、Token 机制、乐观锁 version、业务状态机。

核心要点

常见方案:

方案适用
唯一请求ID + 去重表通用
Token 机制表单提交防重
乐观锁(version)更新操作
状态机有状态流转的业务

数据库层: INSERT ... ON DUPLICATE KEY / 唯一索引

面试回答(2分钟版)

幂等性是指同一个操作执行一次和执行多次产生的效果完全一致。在分布式系统中由于网络重试、MQ重复投递、用户重复点击等场景,幂等性设计非常关键。我总结有四种主要方案。第一是唯一请求ID加去重表,这是最通用的方案,客户端生成全局唯一requestId,服务端在处理前先查去重表判断是否已处理过,可以用Redis的SETNX或数据库唯一索引实现。第二是Token机制,适合表单防重复提交,先从服务端获取一次性Token,提交时携带Token,服务端校验后立即删除,校验和删除必须是一个原子操作(用 DEL 的返回值判断,或 Lua 脚本),否则两个并发请求可能都校验通过。第三是乐观锁,在UPDATE操作中带上version条件,比如UPDATE SET amount=amount-100, version=version+1 WHERE version=当前版本,版本不匹配则更新失败实现幂等。第四是业务状态机,利用状态只能单向流转的特性,比如订单状态从待支付到已支付到已发货,已支付状态下重复的支付请求直接拒绝。需要注意INSERT和a=a+1这类操作天然不幂等,需要通过唯一索引或其他机制显式保证。

追问与易错

追问方向:

  • “幂等和去重一样吗?”→ 去重是幂等的一种实现手段(如唯一键去重),但幂等含义更广,状态机(只允许合法状态转换)和乐观锁(version 条件更新)也是幂等
  • “乐观锁实现幂等的限制?”→ 只适合更新操作(UPDATE SET version=version+1 WHERE version=X),不适合插入操作;高并发下失败率高需要配合重试机制
  • “电商下单链路各环节怎么做幂等?”→ 下单:前端先领 Token 或带客户端生成的 requestId,订单表对订单号/requestId 建唯一索引;支付回调:用状态机条件更新 UPDATE ... SET status=已支付 WHERE id=? AND status=待支付,影响行数为 0 就当重复回调直接返回成功;库存扣减:用 WHERE stock>=n 条件更新或乐观锁 version,并用「去重表插入 + 扣减」放同一个本地事务防 MQ 重复消费

易错点:

  • ❌ 数据库自动保证幂等——INSERT 和 a=a+1 不是幂等的
  • ❌ 加了唯一索引就幂等——唯一索引只能直接挡住重复 INSERT;UPDATE/扣款类操作要把「去重表插入」和业务操作放进同一个本地事务
  • ❌ Redis SETNX 占位后业务失败不用管——占位 key 不删或不带状态,重试请求会被误判为「已处理」;要记录处理状态(处理中/成功/失败),失败时删除或改状态