API幂等性设计全攻略:幂等键、分布式去重与支付安全接口

后端开发

摘要

  • 幂等性是分布式系统的「安全带」:网络重试、消息重复、用户双击,都可能导致接口被多次调用
  • 幂等键(Idempotency Key)是HTTP API幂等的标准做法,Stripe、支付宝、微信支付均已采用
  • 实现幂等有3层防线:客户端幂等键 → 服务端去重表 → 数据库唯一约束
  • 支付、下单、库存扣减是幂等性要求最高的3类接口,面试必考
  • 本文提供从原理到Go实现的完整方案,含Redis + PostgreSQL双保险架构

目录


什么是幂等性,为什么必须做

一句话定义

同一个请求无论执行多少次,结果都和执行一次相同,且不会有多余的副作用。

数学表达:f(f(x)) = f(x)

为什么分布式系统一定会有重复请求

这不是「如果」的问题,而是「什么时候」的问题:

重复来源 典型场景 发生概率
用户重复点击 支付按钮、提交订单 每天都会遇到
网络超时重试 客户端自动重试、网关重试 生产环境常态
消息队列重复投递 Kafka at-least-once、RabbitMQ重投 设计如此
负载均衡重试 Nginx/Envoy上游超时重试 配置不当时频繁
微服务调用链重试 下游超时,上游重发 链路越长越常见

不做幂等的真实代价

2025年某二手交易平台爆出「重复扣款」事故:用户点击支付后网络抖动,客户端自动重试3次,服务端没有幂等校验,导致同一订单扣款3次。涉及金额超过200万元,监管部门介入调查。

幂等性不是优化项,是支付/交易类系统的准入门槛。


幂等性的4种等级

Level 0:天然幂等(HTTP GET/PUT/DELETE)

GET  /api/users/123        → 查询,天然幂等
PUT  /api/users/123        → 全量更新,天然幂等
DELETE /api/users/123      → 删除,天然幂等
POST /api/orders           → 创建,NOT幂等!

POST和PATCH默认不幂等,因为每次调用可能创建新资源或产生新副作用。

Level 1:业务层幂等

通过业务逻辑判断:「这个订单已经创建过了,直接返回之前的结果」。

func CreateOrder(ctx context.Context, req CreateOrderRequest) (*Order, error) {
    existing, err := db.GetOrderByClientOrderId(ctx, req.ClientOrderId)
    if err == nil {
        return existing, nil  // 已存在,直接返回
    }
    return db.CreateOrder(ctx, req)
}

优点:简单直接。缺点:并发时两个请求可能同时通过检查。

Level 2:幂等键 + 去重表

客户端传唯一幂等键,服务端用去重表记录「这个键是否已处理过」。

Level 3:数据库唯一约束(最终防线)

在数据库层加唯一索引,即使应用层有漏洞,数据库也会拒绝重复插入。

CREATE UNIQUE INDEX idx_orders_idempotency_key
ON orders (idempotency_key)
WHERE idempotency_key IS NOT NULL;

生产推荐:Level 2 + Level 3 组合使用。


幂等键设计规范

幂等键的生成规则

规则 说明 示例
客户端生成 由调用方在请求前生成UUID 550e8400-e29b-41d4-a716-446655440000
唯一性 同一业务操作使用同一个键 重试时必须复用原键
作用域 通常绑定 user_id + 操作类型 不同用户可以用相同格式的键
有效期 24-72小时 过期后可复用(极少发生)
传递方式 HTTP Header Idempotency-Key: <uuid>

Stripe的幂等键规范(行业标杆)

POST /v1/charges
Idempotency-Key: uuid-v4-string
Content-Type: application/json

{"amount": 2000, "currency": "usd", "source": "tok_visa"}

Stripe的处理逻辑:

  1. 收到请求,检查幂等键是否已存在
  2. 不存在 → 执行业务逻辑,存储结果,返回201
  3. 已存在且参数相同 → 返回缓存的结果(200)
  4. 已存在但参数不同 → 返回409 Conflict

幂等键 vs 请求ID vs 链路追踪ID

字段 用途 生命周期 是否用于去重
Idempotency-Key 业务幂等 24-72h
X-Request-ID 请求追踪 单次请求
Trace-ID 分布式链路追踪 单次调用链

三者不要混用。Trace-ID每次请求都不同,不能用于幂等。


3层幂等防线架构

客户端                    服务端                              数据库
  │                         │                                  │
  │  Idempotency-Key        │                                  │
  ├────────────────────────→│                                  │
  │                         │  Layer 1: Redis 快速去重          │
  │                         │  SETNX idem:{key} → 成功/失败     │
  │                         │       │                           │
  │                         │       ▼                           │
  │                         │  Layer 2: 幂等记录表               │
  │                         │  INSERT idempotency_records       │
  │                         │       │                           │
  │                         │       ▼                           │
  │                         │  Layer 3: 业务表唯一约束           │
  │                         │  INSERT orders (UNIQUE key)  ────→│
  │                         │                                  │
  │  ←── 返回结果 ──────────│                                  │

Layer 1:Redis SETNX 快速拦截

func (s *IdempotencyService) TryAcquire(ctx context.Context, key string, ttl time.Duration) (bool, error) {
    redisKey := fmt.Sprintf("idem:%s", key)
    acquired, err := s.redis.SetNX(ctx, redisKey, "processing", ttl).Result()
    if err != nil {
        return false, fmt.Errorf("redis setnx: %w", err)
    }
    return acquired, nil
}
  • SETNX 成功 → 首次请求,继续处理
  • SETNX 失败 → 重复请求,查缓存结果返回
  • TTL 设为 24-72h,防止Redis内存泄漏

Layer 2:幂等记录表

CREATE TABLE idempotency_records (
    id              BIGSERIAL PRIMARY KEY,
    idempotency_key VARCHAR(64) NOT NULL,
    request_hash    VARCHAR(64) NOT NULL,  -- 请求体哈希,防参数篡改
    response_status INT,
    response_body   JSONB,
    status          VARCHAR(20) NOT NULL DEFAULT 'processing',
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    expires_at      TIMESTAMPTZ NOT NULL,

    CONSTRAINT uq_idempotency_key UNIQUE (idempotency_key)
);

CREATE INDEX idx_idem_expires ON idempotency_records (expires_at);

状态流转:processingcompleted / failed

Layer 3:业务表唯一约束

ALTER TABLE orders ADD COLUMN idempotency_key VARCHAR(64);
CREATE UNIQUE INDEX idx_orders_idem ON orders (idempotency_key);

即使Layer 1和Layer 2都失效(Redis宕机、应用bug),数据库唯一约束是最后防线。


Go生产级幂等中间件实现

幂等中间件(Gin框架)

type IdempotencyMiddleware struct {
    redis *redis.Client
    db    *sql.DB
    ttl   time.Duration
}

func (m *IdempotencyMiddleware) Handle() gin.HandlerFunc {
    return func(c *gin.Context) {
        key := c.GetHeader("Idempotency-Key")
        if key == "" {
            c.Next()
            return
        }

        ctx := c.Request.Context()

        cached, err := m.getCachedResponse(ctx, key)
        if err == nil && cached != nil {
            c.JSON(cached.Status, cached.Body)
            c.Abort()
            return
        }

        acquired, err := m.redis.SetNX(ctx, "idem:"+key, "1", m.ttl).Result()
        if err != nil {
            c.JSON(500, gin.H{"error": "idempotency check failed"})
            c.Abort()
            return
        }
        if !acquired {
            for i := 0; i < 30; i++ {
                time.Sleep(100 * time.Millisecond)
                cached, err = m.getCachedResponse(ctx, key)
                if cached != nil {
                    c.JSON(cached.Status, cached.Body)
                    c.Abort()
                    return
                }
            }
            c.JSON(409, gin.H{"error": "duplicate request in progress"})
            c.Abort()
            return
        }

        blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
        c.Writer = blw
        c.Next()

        m.saveResponse(ctx, key, c.Writer.Status(), blw.body.String())
    }
}

并发安全的关键点

两个相同幂等键的请求同时到达时:

  1. Redis SETNX 只有一个能成功
  2. 失败者进入轮询等待(100ms × 30次 = 3秒)
  3. 成功者处理完毕后将结果写入DB
  4. 失败者从DB读到缓存结果返回

这个「SETNX + 轮询等待」模式是Stripe、支付宝等大厂的标准做法。


支付场景幂等实战

支付接口的完整幂等流程

用户点击支付
    │
    ▼
客户端生成 Idempotency-Key (UUID)
    │
    ▼
POST /api/payments {amount, order_id}
Header: Idempotency-Key: uuid
    │
    ▼
┌─ 幂等检查 ─────────────────────────────┐
│ 1. Redis SETNX                          │
│ 2. 查幂等记录表                          │
│ 3. 查订单状态(已支付?直接返回)          │
└─────────────────────────────────────────┘
    │ 首次请求
    ▼
┌─ 业务处理(数据库事务)─────────────────┐
│ BEGIN                                   │
│   INSERT payment (status=pending)       │
│   调用支付渠道API                         │
│   UPDATE payment (status=success)       │
│   UPDATE order (status=paid)            │
│ COMMIT                                  │
└─────────────────────────────────────────┘
    │
    ▼
缓存响应到幂等记录表 + Redis
    │
    ▼
返回支付结果给客户端

支付幂等的3个特殊考量

1. 支付渠道也要幂等

调用微信/支付宝时,必须传商户订单号(out_trade_no),同一订单号渠道侧也会去重。

2. 状态机保证

pending → processing → success
                    → failed
                    → timeout → (人工介入)

已 success 的订单,任何重试都直接返回成功,不会再次扣款。

3. 对账兜底

每日对账任务检查:支付渠道账单 vs 本地订单,发现不一致时告警。幂等不是100%可靠的,对账是最后的安全网。


面试高频考点与踩坑指南

面试常问的6个问题

Q1:GET是幂等的,POST为什么不是?

GET是读操作,不改变服务器状态。POST是写操作,每次调用可能创建新资源。但可以通过幂等键让POST具备幂等性。

Q2:Redis宕机了幂等还有效吗?

Layer 1(Redis)失效后,Layer 2(幂等记录表)和Layer 3(唯一约束)仍然生效。Redis是性能优化层,不是唯一保障。

Q3:幂等键过期了,用户又发了一样的请求怎么办?

过期后视为新请求。但业务层应检查订单状态:如果订单已支付,即使幂等键过期也直接返回已支付结果。幂等键是技术手段,业务状态是最终判断依据。

Q4:分布式锁能实现幂等吗?

可以,但不是最优方案。分布式锁解决的是并发互斥,幂等解决的是重复请求。锁的粒度、超时、死锁问题比幂等键复杂得多。推荐幂等键 + 去重表,而非分布式锁。

Q5:消息队列消费端怎么做幂等?

三种方式:(1) 业务唯一键去重(推荐);(2) 消费端维护已处理消息ID表;(3) 数据库唯一约束。Kafka的幂等生产者是生产者侧保障,消费端仍需自行去重。

Q6:幂等和事务什么关系?

幂等保证「多次执行 = 一次执行」,事务保证「多个操作要么全成功要么全失败」。支付场景两者都需要:事务保证扣款和更新订单状态的原子性,幂等保证重试不会重复扣款。

踩坑清单

表现 解法
幂等键由服务端生成 客户端重试时键不同,去重失效 必须由客户端生成并复用
只存成功结果 失败重试时返回旧错误 成功和失败结果都缓存
不设过期时间 Redis/DB数据无限增长 TTL 24-72h + 定时清理
并发不加锁 两个请求同时通过检查 SETNX + 轮询等待
忽略支付渠道幂等 本地幂等了但渠道扣了两次 商户订单号全局唯一

库存扣减场景的幂等设计

支付幂等之外,库存扣减是面试和实战中的第二大高频场景。它与支付的区别在于:支付是「创建新记录」,库存是「更新已有记录」,并发下的竞态更隐蔽。

典型故障:超卖

时间线:
T1: 请求A 读取库存 quantity=1
T2: 请求B 读取库存 quantity=1  (A还未提交)
T3: 请求A 扣减 quantity=0,提交成功
T4: 请求B 扣减 quantity=-1,提交成功  → 超卖!

即使加了幂等键,如果两个请求的幂等键不同(用户两次点击),仍然会超卖。幂等解决重复请求,乐观锁/条件更新解决并发扣减,两者必须组合。

推荐方案:幂等键 + 条件更新

func (s *InventoryService) DeductStock(ctx context.Context, req DeductRequest) error {
    key := req.IdempotencyKey
    if cached, err := s.idem.GetResult(ctx, key); err == nil && cached != nil {
        return cached.Error
    }

    tx, err := s.db.BeginTx(ctx, nil)
    if err != nil {
        return err
    }
    defer tx.Rollback()

  result, err := tx.ExecContext(ctx, `
        UPDATE inventory
        SET quantity = quantity - $1, version = version + 1
        WHERE product_id = $2 AND quantity >= $1
    `, req.Quantity, req.ProductID)
    if err != nil {
        return err
    }
    rows, _ := result.RowsAffected()
    if rows == 0 {
        return ErrInsufficientStock
    }

    _, err = tx.ExecContext(ctx, `
        INSERT INTO idempotency_records (idempotency_key, status, response_body)
        VALUES ($1, 'completed', '{"deducted": true}')
        ON CONFLICT (idempotency_key) DO NOTHING
    `, key)
    if err != nil {
        return err
    }
    return tx.Commit()
}

核心要点:WHERE quantity >= $1 是数据库层的原子守卫,即使应用层有漏洞也不会出现负库存。


分布式环境下的幂等挑战

多实例并发:为什么 SETNX 不够 alone

3个API实例同时收到相同幂等键请求时:

时序 实例1 实例2 实例3
T1 SETNX 成功 SETNX 失败 SETNX 失败
T2 开始处理(耗时5秒) 轮询等待 轮询等待
T3 处理完成写DB 读到结果返回 读到结果返回

如果实例1在写DB前崩溃,实例2和3轮询超时后应返回 409 duplicate in progress 而非重新执行——这要求幂等记录表有 processing 状态,超时后可由补偿任务接管。

Redis 集群故障切换

Redis主从切换时,SETNX记录可能丢失(取决于持久化配置)。因此Redis只做性能加速层,数据库唯一约束才是最终保障。面试时这样说能体现架构深度。

时钟偏移与TTL

幂等键TTL不要用本地时钟做复杂逻辑。统一用 expires_at = NOW() + INTERVAL '72 hours' 由数据库生成,避免多实例时钟不一致导致提前过期。


幂等性与消息队列的配合

Kafka 三层幂等体系

生产者幂等(enable.idempotence=true)
    → 保证单分区内无重复消息
Broker 去重(基于 PID + Sequence)
    → 网络重试不产生重复
消费端幂等(业务层)
    → 跨分区、跨消费者组仍需自行保障

很多候选人面试时说「Kafka保证了幂等」——这是常见误区。Kafka生产者幂等只覆盖单个生产者实例向单个分区的重试场景,消费端重复投递仍需 processed_events 表或业务唯一键。

消息ID vs 幂等键

字段 生成方 用途
Message ID Broker/框架 消息追踪
Business Key 业务(order_id) 消费端去重
Idempotency Key 客户端 API层去重

消费端推荐用 event_id(Outbox表主键)或 order_id + event_type 组合做去重,不要用 Kafka offset(重放时 offset 会变)。


性能优化:幂等检查不能成为瓶颈

高并发支付场景,每次请求都查DB会成为瓶颈。分层优化策略:

第一层:Bloom Filter 快速预判

// 启动时从DB加载近期幂等键到Bloom Filter
// 查询时:BF说不存在 → 一定不存在,直接处理
//         BF说可能存在 → 查Redis/DB确认

Bloom Filter 有误判率(说存在但实际不存在),但不会漏判(说不存在则一定不存在),适合幂等预判。

第二层:异步写回

对于非关键读接口,可以先返回202 Accepted,异步完成幂等记录写入。支付场景不适用——必须同步确认。

第三层:分片幂等表

日订单千万级时,单表 idempotency_records 膨胀。按 hash(idempotency_key) % 64 分64张表,或按日期分区:

CREATE TABLE idempotency_records_202607 PARTITION OF idempotency_records
    FOR VALUES FROM ('2026-07-01') TO ('2026-08-01');

性能基准参考

方案 P99延迟 适用QPS
纯DB唯一约束 15-30ms < 1000
Redis SETNX + DB 3-8ms < 10000
Redis + BF + DB 1-3ms < 50000

企业落地案例:某支付平台幂等改造实录

某中型支付平台(日交易200万笔)在2025年遭遇重复扣款事故后启动幂等改造:

改造前现状

  • POST接口无幂等设计,客户端重试靠「业务状态判断」
  • 仅订单表有 status 字段,无独立幂等记录
  • Redis只做缓存,未参与去重

改造时间线

阶段 周期 动作 效果
Phase 1 2周 支付/退款接口加 Idempotency-Key Header 重复扣款归零
Phase 2 3周 上线幂等中间件,覆盖全部写接口 研发不再重复造轮子
Phase 3 2周 幂等表分区 + 定时清理 存储稳定在20GB
Phase 4 1周 对账系统增加幂等键维度 可追溯每笔重复请求

改造后架构指标

  • 重复扣款事故:0(连续运行8个月)
  • 幂等检查P99延迟:4.2ms
  • 幂等表日增量:约200万行,72小时TTL自动清理
  • 客户端接入率:98.7%(剩余为老旧SDK,网关层兜底生成键)

面试高频考点与踩坑指南(进阶篇)

Q7:接口返回201还是200?

首次成功创建返回201,幂等重放返回200并附带原结果。Stripe明确区分:首次201,重复200。统一返回200也可接受,但要在文档中说明。

Q8:幂等键需要全局唯一还是用户维度唯一?

推荐用户维度:UNIQUE(user_id, idempotency_key)。不同用户可以使用相同格式的键。全局唯一也可以,但索引更大。

Q9:DELETE接口需要幂等键吗?

DELETE天然幂等(删两次结果一样),但建议对「软删除+副作用」(删用户同时清积分)仍用幂等键,因为副作用可能重复触发。

Q10:GraphQL Mutation怎么做幂等?

在HTTP层加 Idempotency-Key Header(与REST相同),或在Mutation参数中传 idempotencyKey 字段。Apollo Gateway可在网关层统一处理。


从零搭建幂等中间件:完整落地清单

如果你要在团队内推广幂等能力,按以下清单逐步落地:

Week 1:规范制定

  1. 所有 POST/PATCH 写接口强制支持 Idempotency-Key Header
  2. 在API文档(OpenAPI)中标注幂等性等级
  3. 客户端SDK内置幂等键自动生成与重试复用逻辑

Week 2-3:中间件开发

// 注册到Gin Engine
r.Use(idempotencyMiddleware.Handle())

// 配置白名单:只读接口跳过
idempotencyMiddleware.SkipPaths("/health", "/metrics")

Week 4:灰度与监控

监控项 Prometheus指标名 说明
幂等命中率 idempotency_cache_hit_total 重复请求被拦截次数
检查延迟 idempotency_check_duration_seconds P50/P99
进行中冲突 idempotency_in_progress_total 需关注的并发冲突

Week 5:全量上线与对账

上线后第一周,每日对账脚本检查:

SELECT DATE(created_at), COUNT(*) AS duplicate_blocked
FROM idempotency_records
WHERE status = 'completed'
  AND created_at > (SELECT MIN(created_at) FROM idempotency_records ir2
                    WHERE ir2.idempotency_key = idempotency_records.idempotency_key
                    AND ir2.id < idempotency_records.id)
GROUP BY DATE(created_at);

幂等性与其他分布式模式的边界

很多候选人混淆幂等、锁、事务的概念。一张表说清楚:

模式 解决什么问题 粒度 典型实现
幂等 重复请求不产生副作用 请求级 Idempotency Key
分布式锁 同一时刻只有一个执行者 资源级 Redis Redlock
事务 多个操作原子性 操作组级 DB Transaction
乐观锁 并发更新冲突检测 行级 version字段
去重 消息不重复消费 消息级 processed_events表

支付场景的标准组合:幂等键(防重复请求)+ 数据库事务(扣款和更新订单原子性)+ 乐观锁(库存并发扣减)。


动手实战:30分钟搭建幂等支付接口

环境准备

docker compose up -d postgres redis
go mod init idempotency-demo
go get github.com/gin-gonic/gin github.com/redis/go-redis/v9 github.com/lib/pq

Step 1:建表(5分钟)

执行前文中的 idempotency_recordsorders 建表SQL。注意先建幂等表,再建业务表,外键依赖顺序不要错。

Step 2:实现幂等中间件(10分钟)

将前文的 IdempotencyMiddleware 代码放入 middleware/idempotency.go。核心逻辑三步:查缓存 → SETNX → 执行业务 → 存结果。

Step 3:接入支付Handler(5分钟)

r.POST("/api/payments", idemMiddleware.Handle(), paymentHandler.Create)

Handler内不需要写幂等逻辑,中间件已保障。

Step 4:验证(10分钟)

# 首次请求
curl -X POST http://localhost:8080/api/payments \
  -H "Idempotency-Key: test-key-001" \
  -H "Content-Type: application/json" \
  -d '{"order_id":"ORD-001","amount":9900}'

# 重复请求(应返回相同结果,不重复扣款)
curl -X POST http://localhost:8080/api/payments \
  -H "Idempotency-Key: test-key-001" \
  -H "Content-Type: application/json" \
  -d '{"order_id":"ORD-001","amount":9900}'

# 并发测试(应用 autocannon 或 wrk)
autocannon -c 10 -d 5 -H "Idempotency-Key: test-key-002" \
  -m POST -b '{"order_id":"ORD-002","amount":100}' \
  http://localhost:8080/api/payments

预期结果:10个并发请求只创建1笔支付。如果创建了多笔,检查SETNX和轮询等待逻辑。

常见调试清单

现象 可能原因 检查方法
重复请求仍创建多笔 SETNX未生效 Redis连接是否正常
第二次请求返回500 未缓存失败结果 检查saveResponse逻辑
并发仍重复 轮询次数不够 增加轮询次数或加DB唯一约束

深度辨析:5个最容易答错的面试题

辨析1:「所有接口都要做幂等」对吗?

不对。幂等有成本(Redis查询、DB写入、存储增长)。正确做法是按接口风险分级:支付/下单/库存扣减必须做;查询接口天然幂等不需要;低频管理后台接口可降级为仅DB唯一约束。

辨析2:「幂等键用UUID够吗?」

UUID v4够唯一,但要注意:客户端必须在重试时复用同一个UUID,而不是每次重试生成新的。建议在SDK层封装——首次调用生成UUID并缓存到内存,重试时自动复用。

辨析3:「返回相同响应体就是幂等」对吗?

不完全对。幂等的核心是「无副作用」,不仅仅是「返回相同」。如果第一次请求还在处理中(processing),第二次应等待或返回409,而不是返回空响应或错误响应。

辨析4:「微服务网关层做幂等 vs 服务层做幂等」怎么选?

推荐服务层做。网关层可以做第一道拦截(性能优化),但服务层必须有最终保障(因为请求可能绕过网关,如内部服务间调用)。两层都做是最佳实践,只做网关层不够。

辨析5:「幂等和防重(防双击)是一回事吗?」

防双击是幂等的子集。防双击通常指出前端按钮置灰+后端幂等键。但幂等还覆盖网络重试、消息重复等客户端无感知的场景。面试时说「防双击」显得思考不够全面,应说「幂等性设计」。


2026年幂等性技术趋势

趋势1:网关层标准化。 Envoy、Kong、APISIX均已支持 Idempotency-Key 插件,可在网关层做第一道去重,减轻后端压力。

趋势2:数据库原生支持。 PostgreSQL社区在讨论内置幂等性扩展。目前仍推荐应用层实现,但关注PG新版本的进展。

趋势3:与OpenTelemetry集成。 幂等检查链路纳入分布式追踪,可观测每次去重的命中率和延迟,纳入SRE Dashboard。

趋势4:AI Agent工具调用的幂等。 LLM Agent通过Function Calling调用支付API时,模型可能因推理重试而多次调用同一工具。API层幂等是最后一道防线——Agent层无法控制,API层必须保障。

给架构师的3句话总结

  1. 幂等性是分布式写操作的准入门槛,不是优化项
  2. 三层防御(Redis + 记录表 + 唯一约束)缺一不可
  3. 面试时从「为什么需要」讲到「怎么实现」再讲到「踩坑和边界」,层次清晰才能拿高分

推荐学习路线(2周)

第1周:理论+基础实现 — 阅读Stripe文档理解幂等键规范 → 实现Redis SETNX去重 → 实现幂等记录表 → 写单元测试验证重复请求。

第2周:生产化+面试准备 — 接入Gin中间件 → 添加Prometheus监控 → 做并发压测 → 整理6个面试题的标准答案 → 画三层防御架构图(面试白板题)。

最后送一句话:在分布式系统中,重复不是意外,是常态。幂等性是你对常态的正确回应。

本文覆盖了从原理、架构、代码实现、性能优化、企业案例到面试考点的完整知识链。建议收藏后按「学习路线」两周实践一遍,面试前重点复习「深度辨析」和「踩坑清单」两个章节。


总结与延伸阅读

API幂等性是后端开发的基本功,也是面试高频考点。记住核心公式:客户端幂等键 + Redis快速去重 + 数据库唯一约束 = 生产级幂等

设计要点回顾

  1. POST接口默认不幂等,必须显式设计
  2. 幂等键由客户端生成,重试时复用同一个键
  3. 3层防线:Redis SETNX → 幂等记录表 → 唯一约束
  4. 支付场景必须结合状态机 + 对账兜底
  5. 并发场景用 SETNX + 轮询等待,不要用分布式锁

相关阅读

权威参考

本站提供浏览器本地工具,免注册即可试用 →

#API幂等性#分布式幂等#Idempotency Key#支付接口设计#面试高频#2026