vault backup: 2026-08-09 19:06:40
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
---
|
||||
tags: [test/review, architecture, arch/idempotency]
|
||||
create time: 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 个要点即可得满分;完全正确需覆盖全部要点。
|
||||
|
||||
## 关联笔记
|
||||
- [[Redis 分布式锁]]
|
||||
- [[ZooKeeper 分布式锁]]
|
||||
Reference in New Issue
Block a user