一致性利器-幂等

幂等基本概念

幂等的数学概念

幂等是一种数学中的概念,其主要有两个定义:

  1. 如果在一元运算中, x x x为某集合中的任意数,如果满足 f ( x ) = f ( f ( x ) ) f(x) = f(f(x)) f(x)=f(f(x)),那么该 f f f运算具有幂等性,比如绝对值运算 a b s ( a ) = a b s ( a b s ( a ) ) abs(a) = abs(abs(a)) abs(a)=abs(abs(a))就是幂等性函数。
  2. 如果在二元运算中, x x x为某集合中的任意数,如果满足 f ( x , x ) = x f(x,x) = x f(x,x)=x,前提是 f f f运算的两个参数均为 x x x,那么我们称 f f f运算也有幂等性,比如求大值函数 m a x ( x , x ) = x max(x,x) = x max(x,x)=x就是幂等性函数。

幂等在开发中的概念

在数学中幂等的概念或许比较抽象,但是在开发中幂等性是极为重要的。可以理解为,对于同一个系统,在同样条件下,一次请求和重复多次请求对资源的影响是一致的,就称该操作为幂等的。比如说如果有一个接口是幂等的,当传入相同条件时,其效果必须是相同的。

此处简单提一下,幂等与并发安全所面临的问题是不一样的。幂等是系统接口对外的一种承诺(而不是实现),承诺多次相同操作的结果都是一样的。并发安全是要保证多个线程对同一个共享资源时,不会出现线程安全性问题,来确保一致性

为什么需要幂等?

如果没有幂等,操作支付接口,重复支付会导致多次扣钱;生成订单的接口,重复生成会生成多个订单。这种重复操作对系统所造成的损失是无法承受的,因此需要实现数学中的幂等操作。

什么情况会产生幂等性问题?

对于一下情况:

  1. 网络波动,可能会引起重复请求。
  2. 用户重复操作,用户在操作时候可能会无意触发多次下单交易,甚至没有响应而有意触发多次交易应用。
  3. 使用了失效或超时重试机制(Nginx重试、RPC重试或业务层重试等)。
  4. 页面重复刷新。
  5. 使用浏览器后退按钮重复之前的操作,导致重复提交表单。
  6. 使用浏览器历史记录重复提交表单。
  7. 浏览器重复的HTTP请求。
  8. …………

常见的实现幂等的方法

解决办法分为两个方向,一个方向是客户端防止重复调用(由前端解决),一个方向是服务端进行校验。

客户端防止重复调用的方法就是提交之后将按钮置灰或者Loading状态,消除用户重复操作造成的问题。虽然客户端实现起来比较简单,但这种方法防止重复提交并不是绝对可靠的。

下面主要来介绍服务器端进行校验的方法:

基于Token机制的实现

Token机制应该是适用范围最广泛的一种幂等设计方案了,具体实现方式也很多样化。但是核心思想就是每次操作都生成一个唯一Token凭证,服务器通过这个唯一凭证保证同样的操作不会被执行两次。这个Token除了字面形式上的唯一字符串,也可以是多个标志的组合(比如上面提到的状态标志),甚至可以是时间段标识等等。

举个具体的例子:

  1. 客户端访问业务页面的时候,会请求服务端生成一个全局唯一的ID作为Token并将其放入到Redis中(具体场景具体分析,也可以放入本地缓存),同时会把这个Token返回给客户端会。
  2. 客户端调用业务功能发送请求时需要携带这个Token。
  3. 服务端还会检查这个Token是否在Redis中,有的话证明业务还没执行完成,可以执行当前业务,并删除Redis中对应的Token。
  4. Token不存在Redis中的话,证明业务已经执行完成,直接返回重复操作。

在这里插入图片描述

这种方法可能存在以下问题:

  1. Token的生成算法可能不够安全。如果Token可以被轻易破解,可能会导致系统被攻击。可以采用美团的Leaf或百度的uid-generator去生成。
  2. 如果Token存储在内存中,系统重启或者崩溃会导致已生成的Token全部失效,这样在系统恢复后,之前的Token就会失效,导致无法实现幂等。
  3. 如果Token的过期时间设置过短,可能会导致请求因为Token已过期而被拒绝,从而无法实现幂等。

基于MySQL的唯一索引实现

利用数据库唯一索引机制,当数据重复时,插入数据库会抛出异常,保证不会出现脏数据。

举个具体的例子:

  1. 服务端建立一张去重表,其中某个字段为需要建立的唯一索引。
  2. 客户端去请求服务端,服务端会将这次请求的一些信息插入这张去重表中。
  3. 因为表中某个字段带有唯一索引,如果插入成功,证明表中没有这次请求的信息,则执行后续的业务逻辑。
  4. 如果插入失败,则代表已经执行过当前请求,直接返回。

在这里插入图片描述

这种方式可能存在以下问题:

  1. 在面对高并发的场景,会出现大量的请求打到数据库上,导致服务崩溃。
  2. 为了保证唯一索引的唯一性,可能占用较大的存储空间,减缓搜索速度。

基于Redis的SETNX命令实现

在Redis中,可以使用SETNX命令来实现幂等操作。SETNX命令的作用是设置一个键值对,当且仅当该键不存在时才设置成功,否则不做任何操作。

  1. 当请求到达时选择一个某些属性构成唯一标识,并使用Redis的SETNX命令尝试设置键值对。
  2. 如果改建不存在,证明还没有执行过该操作,执行该操作,并对其设置过期的时间,避免占用大量的内存。
  3. 如果该键已经存在,说明当前操作已经执行过了,直接返回结果。

在这里插入图片描述

这种方式可能存在以下问题:

  1. 使用Redis中的SETNX命令来实现幂等操作,需要在程序代码中特别处理键的存在和过期时间等情况,增加了代码的复杂度和维护成本。
  2. 需要依赖于Redis的可靠性和可用性。如果Redis出现故障或不可用,就可能导致幂等操作失效。

基于乐观锁的实现

该方式适用于更新已存在数据的场景。其基本思路是:在更新数据时,先检查数据的版本号是否与当前版本号相同,如果相同则进行更新,否则认为操作冲突,更新失败

  1. 在数据表中添加一个版本号字段。
  2. 当进行更新操作时,先从数据表中读取当前记录的版本号。
  3. 在更新数据时,同时将当前版本号传递给数据库,数据库会检查该版本号是否与当前版本号相同,如果相同则进行更新,否则认为操作冲突,更新失败。

这种方式可能存在以下问题:

  1. 需要额外处理保证,更新完后不会再有相同的请求进入
  2. 并发量高的话会导致大量请求不断读取数据库中的版本号字段

基于状态机的实现

基于状态机实现幂等的思路是,在系统中引入一个状态机,并将幂等操作作为状态机中的状态转移,每次执行幂等操作时,都需要先检查当前状态是否可以执行,如果可以执行,则进行状态转移并执行操作,否则忽略操作。

具体流程:

  1. 根据业务需求定义状态机的状态和状态转移规则,将幂等操作作为状态机中的一个状态。
  2. 在系统中引入状态机,将状态机的状态保存在数据库或者缓存中,以便在系统重启或者故障恢复时能够恢复状态。
  3. 在执行幂等操作时,先查询当前状态机的状态,检查当前状态是否可以执行幂等操作,如果可以执行,则进行状态转移并执行操作,否则忽略操作。
  4. 在执行幂等操作后,更新状态机的状态,以便下一次执行幂等操作时能够正确地判断状态是否允许执行。

这种方法存在以下问题:

  1. 实现起来比较复杂,需要考虑状态机的设计和实现细节,同时还需要进行状态机的状态同步和恢复,对系统的性能和可靠性要求较高。

以上是初学者的看法,各位大佬有什么想法,欢迎讨论。

参考文献:

  1. 幂等 (idempotence) 的概念
  2. 阿里面试官:接口的幂等性怎么设计?
  3. 什么是接口幂等性?为什么会产生这个问题?如何保证接口幂等性?
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值