1.概念

幂等性是数学概念,即 f(x)=f(f(x))。在计算机领域,则是意为 对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的。在调用接口时,总有一些特殊情况会导致接口进行重复的调用,如果不对这些情况做出处理,就可能导致脏数据,甚至是业务流程上的问题。

2.不满足幂等性的影响

试想这样的一种场景:在电商平台上支付后,因为网络原因导致系统提示你支付失败,于是你又重新付款了一次,等完成后检查网银发现被系统扣了两次款,这是一种什么样的体验?

造成上述问题的原因可能有很多:

  • 比如第一次付款时实际支付成功,但是信息返回时网络中断导致系统误判;
  • 比如第一次付款的确失败了,但第二次付款时发生意外,导致支付请求被重复发送等等。

在一次支付的过程中,每个环节都有可能会发生问题,我们要如何规避这类问题引发的分险?幂等性是解决这类问题的方案之一。

3.从哪几个方面保证幂等性

幂等性的保证需要从两个方面下手:

1. 空间维度

空间维度定义了接口幂等的范围;比如我是订单数据,范围就是不同的用户 id 和订单 id(相同订单 id 不能重复下单),或者是商品(相同商品不能重复下单)

2. 时间维度

时间维度则是定义了接口幂性的有效期。比如,订单需要保证永久性幂等,永远不能拥有相同的订单,部分业务只需要保证一段时间的幂等性。

4.具体保证幂等性方案

如果想要保证幂等性,首先要知道什么场景下才会出现幂等性问题。常见场景如下:

  • 接口的幂等性问题通常出现在修改操作或者增添操作上,而针对查询与删除操作,通常不会拥有此种问题
  • 业务上要求一致性较高的场景。(比如支付、下订单、抢票等..)

4.1 select + insert

解决永久性幂等问题的一种解决方案。就是在执行操作前,判断之前是否已经操作过了,比如下单之前检查是否具有相同的订单 ID 已经存在,这样就可以避免重复提交的问题。永久性幂等问题通常与业务逻辑具有强关联,所以校验通常放置在参数校验上。

@注意: 针对并发问题带来的脏数据的问题无法解决,需要通过下面几种方式来实现。

4.2 唯一索引

这是最容易想到的方式。页面的数据通常只能被提交一次,多次提交可能会产生脏数据。比如,同一名称的商品只能被创建一次,为了防止创建多次,可以给商品名称添加唯一索引。当在添加一个已有名称的商品时,数据库插入操作就会因为唯一索引而引发异常,避免了脏数据的产生。类似的案例还有博客点赞,订单创建等场景。

唯一索引不仅可以解决并发下的脏数据问题,也可以解决永久性幂等的问题。

缺点: 无法适用分布式存储系统,需要维护数据库的唯一索引,多的情况下不容易管理

4.3 分布式锁

如果是分布式系统,全局的唯一索引就很难构建,此时可以使用分布式锁的方式解决此类问题。我们可以在执行第操作时先获取分布式锁,做完操作后,再将分布式锁释放。这样可以解决高并发性下的幂等性问题。

@注意: 需要配合 select+insert 使用,如果不配合,无法解决表单重复提交的问题。

4.4 token 机制

token 机制是一种比较常用的机制,核心原理在于给每个操作执行前,需要去服务中获取一个 token,执行操作时需要携带 token 进行操作。如果发现 token 存在,则使用 token,并将其置为已使用,并执行操作,执行完毕后并且会将 token 对应的执行结果存储起来。否则将会检查是否存在执行结果,直接取出。

比如下单操作需要一次进行添加订单、更改库存、更改优惠券三个操作。每个操作执行前都去使用 token 验证该操作是否已经执行,从而防止重复执行的问题。并且,缓存的结果也可以用于事务控制(如果下单失败,增加记录的库存和优惠券)。

缺点:使用 token 机制,那么就会意味着在需要保证幂等性的接口在被调用前,必须先调用接口获取 token

4.5 MVCC 机制

MVCC(Multi-Version Concurrency Control) 多版本并发控制。在数据更新时需要去比较持有数据的版本号,版本号不一致的操作无法进行更新,更新成功后版本号将会发生变化。

@注意: 每一个version只有一次执行成功的机会,一旦失败必须重新获取。

4.6 状态机幂等

所谓状态机,就是任务或者业务在执行的过程中,拥有的状态以及状态的变更图。在执行某个操作前,需要先对当前状态进行验证,如果状态不是该有的状态,则拒绝操作。