幂等的核心是同一请求(带唯一标识)无论执行多少次,业务状态只变更一次;关键不在不报错,而在不产生副作用,需客户端生成幂等键、服务端校验、db事务内完成状态变更与幂等校验。

为什么 idempotent 不能只靠重试加 try/except
重试 + 捕获异常看似能防重复,但实际只是掩盖了状态不一致。比如支付接口被调两次,第一次成功扣款但网络超时没收到响应,第二次重试又扣了一次——这不是幂等,是资损。
真正幂等的核心是:**同一请求(带唯一标识)无论执行多少次,业务状态只变更一次**。关键不在“不报错”,而在“不产生副作用”。
- 必须把幂等键(如
idempotency_key)作为业务逻辑入口参数,不是日志或中间件里偷偷记一笔 - 幂等键要由客户端生成(如 UUID),服务端只校验、不生成——否则分布式下无法保证跨请求一致性
- 存储校验点必须和业务更新在同一个事务里(如 PostgreSQL 的
INSERT ... ON CONFLICT DO NOTHING),否则存在竞态窗口
用数据库唯一约束实现最简幂等(Python + SQLAlchemy)
这是中小系统最稳的起点,不依赖外部组件,靠 DB 原子性兜底。重点不是“怎么写 ORM”,而是怎么设计约束字段和事务边界。
常见错误:只在业务表加唯一索引,但没把幂等键和业务主键组合建索引,导致插入时冲突判断失效。
立即学习“Python免费学习笔记(深入)”;
Magento是一套专业开源的PHP电子商务系统。Magento设计得非常灵活,具有模块化架构体系和丰富的功能。易于与第三方应用系统无缝集成。Magento开源网店系统的特点主要分以下几大类,网站管理促销和工具国际化支持SEO搜索引擎优化结账方式运输快递支付方式客户服务用户帐户目录管理目录浏览产品展示分析和报表Magento 1.6 主要包含以下新特性:•持久性购物 - 为不同的
- 幂等键字段类型建议用
CHAR(32)(存 UUID)或TEXT,避免截断;不要用VARCHAR加长度限制后隐式截断 - 唯一约束必须包含
idempotency_key和业务关键状态字段(如order_id+status),否则无法区分“同一订单不同操作” - SQLAlchemy 中用
session.execute(text("INSERT INTO ... ON CONFLICT DO NOTHING"))比merge()更可控——后者可能触发无意义 UPDATE
Django 视图层如何安全接入幂等头(Idempotency-Key)
Django 默认不解析这个 header,直接读 request.META.get("HTTP_IDEMPOTENCY_KEY") 就行,但坑在后续链路怎么传、怎么验。
典型翻车:视图里取到 key 后,把它当普通参数传给异步任务(如 Celery),结果任务重试时 key 丢了,或者多个任务并发写同一 key 没锁住。
- 必须在视图函数开头就校验 key 是否为空或格式非法(如非 UUID),空值直接
return HttpResponse(400),别往后传 - 如果走异步,key 必须作为任务参数显式传递,且任务函数开头立刻做幂等检查——Celery 的
retry不会自动带原始 header - 不要用 Django cache(如 Redis)单独存 key 状态,除非你同时用 Lua 脚本保证 set+check 原子性;否则
get和set之间有竞态
Redis 实现高性能幂等(SET key value EX 3600 NX)的三个硬限制
Redis 快,但不是万能解药。它适合“纯校验型”幂等(如发短信、发通知),不适合“需回滚”的业务(如库存预占)。
错误用法:用 INCR 计数然后判断是否 >1——这无法防止并发写入,两个请求几乎同时 INCR 都返回 1。
- 必须用
SET key value EX 3600 NX(NX是关键),value 建议存请求摘要(如hashlib.sha256(body.encode()).hexdigest()),用于事后审计 - 过期时间不能拍脑袋定:要大于最长业务耗时(含下游超时重试),但也不能太长(如 7 天),否则 key 泛滥,Redis 内存扛不住
- Redis 故障时必须降级策略——比如切到本地内存缓存(
lru_cache)或直接拒绝(503 Service Unavailable),绝不能静默跳过幂等检查









