← 每日面试题 学习看板

为什么不建议用数据库唯一性约束做幂等控制?

面试题/02架构设计题/02分布式/07接口幂等与安全/为什么不建议用数据库唯一性约束做幂等控制?.md

面试记录 2026-10-08

题目:为什么不建议用数据库唯一性约束做幂等控制?

难度:medium

标签:arch/distributed/idempotent, architecture/idempotent, difficulty/medium

评分:⭐⭐⭐⭐☆(4/5)

我的回答

所谓幂等,就是为了保证同样的请求过来多次,得到同样的结果,而不是多次请求之后得到不同的结果。这不仅仅是数据库层面要做的,而是在业务层也要做相应的处理。第一,你要确定相同,这个请求是相同的。你需要通过特定的业务模式或者什么的去保证了解这个请求跟上一个请求或者上几个请求是一样的,来判定这个请求已经来过了,而不再进行新的处理,或者处理之后得到一样的结果。用数据库唯一约束做索引,这种情况只适用于幂等的少数一些情况,而不会涉及所有,不能统一的处理幂等的逻辑。并不能真正保证请求的幂等。想要达到幂等,需要包括识别、特殊处理,然后保证。其实核心应该在识别跟处理两个项目上,然后通过这两项最终保证每次请求的结果是同样的。比如查询,你每次查询使用相同结果,在没有数据更新的情况下都会是一样的。但是更新操作就不一定是怎么回事了,比如扣减,每次扣减库存,如果连续的进行扣减,就会导致扣减多次,最终库存扣错。这时候的幂等你就没法通过数据库的唯一性约束去保证这个幂等。你需要通过请求的业务识别,识别到这个请求是跟之前重复的,才能保证你的幂等性,也就是说这次不会出现重复的扣减。这就是我理解的为什么不要用数据唯一性做幂等。

点评

优点:抓住幂等本质(相同请求相同结果);区分业务层识别 vs 数据库约束;举例到位(库存扣减);三段式框架(识别→处理→保证)。

不足:未说清唯一约束的技术局限(只适用于写路径、异常处理侵入、锁竞争、无业务语义);未提主流幂等方案(去重表/Redis SETNX/Idempotency-Key/乐观锁);未区分"不能用" vs "不够用"。

补充:唯一约束可用于订单号防重等插入型场景,但读操作/更新操作/分布式场景都无效;主流方案:业务去重表、Redis SETNX+TTL、Idempotency-Key 请求头、乐观锁版本号、状态机单向流转。

完整讲解

第一部分:知识重点剖析

核心考点定位:考察对幂等本质的理解 + 唯一约束的能力边界 + 多种幂等方案的选型能力。

知识点拆解:

  1. 幂等本质:同一操作执行 N 次,结果与执行 1 次等价。分三类:
  1. 唯一约束的能力边界:
  1. 主流幂等方案对比:
按「本质定义 → 唯一约束边界 → 主流方案」递进。

第二部分:考察方向与延伸递进

考察方向:

延伸递进面试题:

  1. 幂等键(Idempotency-Key)是怎么设计的?如何避免攻击?
  1. 消息队列消费幂等如何设计?和接口幂等有什么差异?
  1. Redis 分布式锁 + 幂等控制,是重复的吗?

第三部分:面试者参考回答(300-600 字口语化)

面试官您好,这个问题我先说结论:唯一约束能做幂等,但只覆盖很小的场景,不能作为统一方案。

先讲幂等本质:同一操作执行 N 次,结果和执行 1 次等价。它不是数据库能单独保证的,需要业务层识别重复请求、走幂等处理路径。

唯一约束的边界是:

而且用唯一约束还有几个副作用:冲突走异常路径,代码里到处 try-catch;唯一索引写入有锁竞争,热点 key 会拖慢写入;没有业务语义,不记录请求的上下文、时间窗、处理结果。

所以主流方案是分层的:

  1. 业务去重表:idempotent_key + status + result 存下来,简单可靠,适合强一致场景;
  2. Redis SETNX + TTL:SET key value NX EX 5,防短时重复提交,比如表单提交;
  3. Idempotency-Key 请求头:API 层统一拦截,Stripe 就是这么做的,客户端传 key,服务端存 key → 结果的映射;
  4. 乐观锁版本号:UPDATE WHERE version = ?,防并发更新覆盖;
  5. 业务状态机:状态单向流转(未支付 → 已支付),重复支付请求直接返回失败,天然幂等。

实际项目中通常是组合使用——入口用 Redis 挡住短时重放,业务层用去重表 + 状态机保证最终一致性。唯一约束更多是作为最后一道兜底(订单号唯一),而不是主力方案。