Files
autumn-recruitment/05.架构/idempotency/幂等设计方案_test.md
T

6.7 KiB
Raw Blame History

tags, create time
tags create time
test/review
architecture
arch/idempotency
2026-08-09 12:00

幂等设计方案 — 测试题

概述

本测试覆盖六种主流幂等方案(唯一索引防重、Token 预获取、乐观锁版本号校验、状态机校验、网关 Request ID 去重)以及三层防御最佳实践。共 10 道题目(6 选择 + 3 填空 + 1 简答)。


一、选择题(6道,由浅入深)

难度阶梯: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景

Q1(基础)— 考察定义层面

以下哪个描述最准确地表达了"幂等"的概念?

A. 相同的请求只在系统中出现一次 B. 多次执行同一操作与执行一次的效果相同 C. 使用 Redis SETNX 来防止重复执行 D. 数据库的 UNIQUE INDEX 约束

Q2(基础)→

以下哪种幂等方案不适合短延迟批处理场景(如批量导入数据)?

A. 数据库唯一索引防重 B. Token 预获取模式 C. 乐观锁版本号校验 D. 网关层 Request ID 去重

Q3(进阶)— 核心原理

Lua 脚本 if redis.call("GET", key) == expected then redis.call("SET", key, "USED") end 在 Token 幂等模式中的核心作用是什么?

A. 设置 Token 的过期时间 B. 原子性地检查 Token 是否有效并标记为已使用 C. 生成一个新的 Token D. 删除已经过期的所有 Token

Q4(进阶)— 比较/辨析

以下关于各幂等方案的对比中,哪一个是错误的?

A. 唯一索引防重的实现复杂度低但会额外占用存储 B. Token 预获取可以防止 CSRF C. 状态机校验不需要额外的数据结构 D. 网关 Request ID 方案可以保证跨重试窗口外的重复请求也被拦截

Q5(深入)— 场景推理

某支付系统收到同一笔交易的重复扣款请求(同一个 biz_no)。以下哪种组合方案能提供最可靠的防护?

A. 仅用数据库唯一索引 B. 仅用网关 Request ID C. 三层防御:网关 Request ID → Token 模式 → 数据库唯一索引兜底 D. 仅用乐观锁版本号

Q6(深入)— 源码级/边界场景

使用唯一索引做幂等时,以下哪种做法可以正确避免 ToCToU 竞态条件?

A. SELECT count(1) WHERE biz_no='xxx',如果为 0 则 INSERT B. 直接 INSERT,利用 Duplicate entry 错误判断已被处理过 C. SELECT FOR UPDATE 后手动插入 D. 先在内存 Map 中记录已处理的 biz_no


二、填空题(3道)

F1 — 填空1

去重是_____,幂等是_____。去重是手段,幂等是目标——用唯一索引去重是为了达到幂等效果。

提示: 原文中有明确的区分:"XX是强调语义...YY是强调数据..."

F2 — 填空2

Token 预获取模式中,客户端先调用 _____ 接口获取一次性 Token,服务端存入 Redis 并设置过期时间(如 5 分钟),然后客户端携带 Token 发起实际业务请求。

提示: 回想原文中描述的完整流程步骤。

F3 — 填空3

Redis SETNX 不适合单独做业务幂等的原因是:Redis 非持久化宕机可能丢数据;而且 SETNX 只能保证互斥,不能保证"之前是否已经_____过"。

提示: 考虑 SETNX 的能力边界。


三、简答题(1道)

S1

某电商下单接口面临用户重复点击导致重复创建订单的问题。请设计一个完整的幂等方案,要求:

  1. 从流量入口到数据存储的全链路设计
  2. 说明每层的作用和依赖关系
  3. 讨论失败降级策略

答题框架提示:

  1. 第一道防线:什么位置做什么事
  2. 第二道防线:什么机制保证什么
  3. 第三道防线:最终兜底
  4. 异常场景分析

参考答案与解析

选择题答案

题号 正确答案 解析
Q1 B 幂等的定义是"多次执行与执行一次效果相同"。A 是去重的定义,C 和 D 是实现幂等的手段而非定义本身。
Q2 B Token 模式需要多一次 API 调用(先领 token 再提交),对于批量导入这种高频操作会严重拖慢用户体验。此时唯一索引方案更合适。
Q3 B Lua 脚本将 GET 和 SET 打包成原子操作——不存在 ToCToU 竞态。如果分开执行两个步骤,其他线程可能在中间时刻拿到同一个 Token。
Q4 D D 是错误的!网关 Request ID 只保护单次请求窗口内的重复(通常设定 TTL 如 300 秒),超出窗口的重复请求无法覆盖。这是其固有限制。
Q5 C 三层防御互为补充:网关层拦截大部分重复流量降低后端压力,Token 模式控制高风险操作的不可重入性,数据库唯一索引作为最后防线保证最终一致性。任何一层被突破都有下一层兜住。
Q6 B 直接 INSERT 利用 Duplicate entry 错误是最原子性的做法。A 是典型的 ToCToU——检查和插入之间有竞态窗口。C 的 SELECT FOR UPDATE 也可以但不如 D 简洁高效。D 的内存 Map 不解决分布式问题。

填空题答案

题号 答案 解析
F1 数据;语义 去重强调数据层面——相同的请求只出现一次;幂等强调语义层面——多次执行结果相同。去重是手段,幂等是目标。
F2 /token/generate Token 预获取流程:① 客户端调用 /token/generate 获取一次性 Token;② 服务端存入 Redis,UNUSABLE 状态,5 分钟过期;③ 客户端携带 Token 发起业务请求;④ 服务端 Lua 原子消费 Token。
F3 成功执行 SETNX 只能保证"此刻没人持有这个 key",但不能回答"之前是否已经成功处理过这个请求"。除非你在 Redis 里也存一份状态——那本质上就是把数据库该做的事搬到了 Redis。

简答题参考答案

S1:参考答案要点:

  1. 第一道防线(网关层):客户端在 HTTP 头中携带 X-Request-ID(UUID),网关层用 Redis SETNX reqid:id 1 EX 300s 做去重。重复请求直接从缓存返回之前的响应。
  2. 第二道防线(业务层):对支付等高风险接口采用 Token 模式——用户必须先领取一次性 Token 才能提交订单,Token 原子消费确保不可重入。
  3. 第三道防线(存储层):order_idempotent 表以 biz_no 为唯一索引。INSERT 时 Duplicate entry → 查询已有结果返回。
  4. 异常降级:网关层 Redis 挂了时放行(靠后续两层兜底);Token 服务挂了时切换到 Request ID 模式(用户体验下降但不会中断);数据库层永远生效因为它是物理约束。

评分标准:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点。

关联笔记