分布式系统幂等设计

幂等性

幂等(idempotent、idempotence)是一个数学与计算机学概念,对于同一操作,无论是发起一次请求还是多次请求,最终的执行结果是一致的。不会因为多次请求而产生副作用,不影响系统状态。

在分布式环境中,网络环境更加复杂,因前端操作抖动、网络故障、消息重复、响应速度慢等原因,对接口的重复调用概率会比集中式环境下更大,尤其是重复消息在分布式环境中很难避免。

数据库 DML 幂等性

SELECT 查询天然幂等;
DELETE 删除也是幂等,删除同一个数据多次其效果一样;
UPDATE 直接更新某个值时,幂等;
UPDATE 更新累加操作的的结果,非幂等
INSERT 操作会每次都新增一条,非幂等

RESTful 中幂等性

HTTP Method Idempotent
OPTIONS yes
GET yes
HEAD yes
PUT yes
POST no
DELETE yes
PATCH no

POST、PUT 区别(PUT 需要支持幂等)

解决方案

通常由客户端和服务端约定一个唯一ID,在请求中携带唯一 ID,然后由服务端进行验证其唯一性。服务端会保存已经收到的唯一 ID,若出现唯一性冲突,就说明是处理过的请求,则可以返回给客户端之前的结果或是提示客户端请求重复。

对于幂等键通常是业务单据 ID 等,如何分布式环境下生成全局唯一 ID 也是需要考虑的。

幂等键:分布式唯一ID

以 MySQL 自增 ID 方案为例:

  • 多个 mysql DB:每个 DB 是独立的发射器,相互产生的ID各不相同,假设集群一共有 4 个DBs,DB0 产生的 ID%4=0,DB1 的 ID%4=1,…,DB3 的 ID%4=3;
  • 解决 DB 性能问题:一次从 DB 获取 N 个 ID,N 可配置,对应 DB 的操作就两个:然后在内存中记录 4*N 个 ID 中计算出模 4 为 DB_index 的 N 个 ID,放在内存 list 中。
    • query 当前 ID
    • update current_id = current_id + 4*N where current_id=id_queried
  • 服务可水平扩展:每个服务会访问集群内的4个 DBs,实现对 DB 访问的负载均衡;
  • 高可用:合理配置 N,单个 DB 可以满足足够高的 ID QPS 需求,因此,即使4个 DB 中的3个都挂了,单个 DB 完全能够撑住任意高的 ID QPS,服务还是 OK 的;
  • 接口低延迟:调用方获取 ID 是从内存list中直接获取,list 有最低长度校验,低于该值,将异步触发从 DB 中获取 ID;
  • 预加载:启动时,发号服务会加载 30W 个 ID 到内存 list 中,以保证,例如按照 QPS 峰值 500 计算,若所有 DB 全挂,内存list还能支撑 30 分钟的 ID 使用量;
  • 降级:当所有 DB 全挂,并且内存 list 里面的ID全部用光时,可以降级到 timestamp 方案;
  • 异地多活:要求跨 IDC ID 不重复。因为有每个集群 IDC 标识不同,可保证 ID 不重。

更多可以参考美团的分布式 ID 方案滴滴TinyId百度 uid-generator

流程

服务端幂等处理正常流程一般是:

  1. 判断幂等键是否已经存在,存在就不执行后续了,不存在则保存(可以保存在单独的幂等表);
  2. 处理业务逻辑;
  3. 返回结果。

这里还有一个细节是如果阶段 1 保存幂等键成功,但是阶段 2 执行失败了如何处理,因为可能导致无法重试的情况。所以要么不支持重试,要么把幂等键保存和业务逻辑放到同一个事务。

方案上常用的包括:

  1. 数据库利用唯一索引建立幂等表(推荐,支持事务);
  2. 利用乐观锁(如带版本号更新);
  3. 分布式锁实现(如 redis);
  4. 悲观锁实现(正常不用,性能较差)。

实例

以扣库存举例实现幂等,可以以 order_id, product_id 作为唯一索引 (且先 insert 能减少锁时间):

  1. 插入流水

    insert into stock_log_table(order_id, product_id, count) values… // 插入一条(订单ID,商品ID,数量)流水。

  2. 更新库存

    update stock_table set stock=stock-N where product_id=xxx and stock >= N

参考

分布式系统互斥性与幂等性问题的分析与解决

Leaf——美团点评分布式ID生成系统

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

Scroll to Top