5.3.4 幂等性实现

由于分布式系统中可能会存在超时重试的情况,柔性事务大都采用最大努力交付机制解决超时问题,因此柔性事务中的操作必须是幂等的。

幂等是来源于数学中的一个概念,用数学语言表达就是 f(x)=f(f(x)),计算机中指的是一个操作多次执行的结果与其执行一次的结果相同。业务具有幂等性可以规避数据不一致和重复处理的问题,譬如一个退款的接口,如果不加以控制,多次请求产生多次退款会造成无法承受的损失。

实现系统的幂等性有多种方式,笔者介绍一种常用的方式:使用全局唯一 ID 方案。利用全局唯一 ID 及数据库主键唯一特性解决重复提交的问题,对于相同的 ID 重复插入时,产生 result in duplicate entry for key primary 错误,这种方式的流程如下。

生成全局唯一 ID 最合适的是使用 snowflake(雪花算法,取自世界上没有两片相同的雪花之意),这是 Twitter 开源的分布式自增 ID 算法,使用分布式部署的情况下每秒可生成百万个不重复、递增 id。

另外一种方式是Token 机制,这种方式和全局唯一ID有点类似,不过增加了一个校验 Token 是否有效的逻辑,笔者以一个订单系统举例说明。

  • 订单系统提供一个发放 Token 令牌。Token 生成可以使用上面提到的 snowflake 算法生成。
  • 在订单页面,调用获取 token 接口,并对该 token 设定一个失效期
  • 提交订单时,将该 token 作为参数提交给后端订单系统,后端判断该 token 是否存在
    • 如存在,则为第一次提交,放行并删除 token
    • 如不存在,第二次提交,阻拦该请求

在高并发的环境中,需要注意 token 的获取和删除要使用原子操作。

2. 幂等下 ABA 的问题

先看一下数据库中幂等和非幂等性的操作:

幂等操作

update order set price = 100 where id = 1;

非幂等

update order set price = price+1 where id = 1;

在高并发的场景中,计算型更新会产生非幂等性问题。以支付环节为例,说明幂等性问题:

  1. 用户下单一个 100 块钱的商品,在支付前与商家沟通这打个 9 折。
  2. 商家操作出错,将价格改成了 8 折,改完后发现改错了,再修改成 9 折,对于订单系统这两次都修改成功了。
  3. 但是由于网络出错,第一次修改通知产生了重试或者其他逻辑,覆盖了后面 90 元的推送。
  4. 最终用户支付的价格,是错误的 80 元。

对于这种典型的 ABA 问题,通常可以使用乐观锁来解决, 即在数据中加一个版本号的概念,版本号不一致则产生异常处理,我们看一下使用乐观锁的处理逻辑。

乐观锁逻辑

数据库原句 注意保证原子性操作

update order set price=80,version=version+1 where order=1 and version=1

从上面的流程图中, 当商家更新失败时,产生一个错误的逻辑,或者刷新页面,或者其他方式,重新获取最新的版本号就行修改,即可解决 高并发中 ABA 问题。

总字数:938
Last Updated:
Contributors: isno