面向 django 的底层教程:高并发下的“幂等 + 锁 + 约束”三件套

面向 django 的底层教程:高并发下的“幂等 + 锁 + 约束”三件套

做接口抗并发,先从“写路径”入手:让同一业务请求只生效一次,且代价最小。下面给出从数据库到应用层的闭环方案。

  1. 数据库唯一约束(最后兜底) 为业务幂等键建唯一索引,如 order_no(user_id, biz_key)
CREATE UNIQUE INDEX uniq_pay ON pay(order_no);

PostgreSQL 可用 upsert:

INSERT INTO pay(order_no, amount, status)
VALUES (%s,%s,'CREATED')
ON CONFLICT (order_no) DO NOTHING;

返回受影响行数即可判断是否首次写入。

  1. 乐观锁(行版本保护) 为行加 version 字段:
rows = Pay.objects.filter(id=pid, version=v).update(status='PAID', version=v+1)
if rows == 0: raise ConcurrencyError

避免长事务导致的表级锁竞争。

  1. 分布式锁(短临界区) 对跨表聚合、库存扣减,用 Redis 短锁:
with redis.lock(f"lock:inv:{sku}", timeout=3, blocking_timeout=1):
    deduct_inventory(sku, qty)

锁只包裹最小临界区,防止放大延迟。

  1. 幂等键传递与过期 把 Idempotency-Key 作为请求头下发,服务端以 key -> result 写入 Redis(TTL=30min),重复请求直接回放结果,既省数据库,也保证一致响应。

  2. 任务重试与去重 Celery 任务签名加去重键:

sig = task.signature(args, kwargs, immutable=True)
if not redis.setnx(f"dedup:{sig.id}", 1): return
redis.expire(f"dedup:{sig.id}", 1800)
sig.apply_async(retry=True, max_retries=5)
  1. 观测与回放 记录“第一次命中/重复回放/冲突异常”的计数与耗时,埋点到 Prometheus;当冲突率上升,优先检查唯一索引与锁粒度。

三件套组合顺序是:唯一约束兜底 → 乐观锁保护更新 → 分布式锁限临界区 → 幂等回放降噪。做到这点,接口就能在抖动与重试里保持“只成功一次”。

评论 0