Java教程

聊聊幂等

本文主要是介绍聊聊幂等,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

什么是幂等

这里的幂等是参考的数学上的一个概念,在服务中的定义为多次执行所产生的影响均与一次执行的影响相同。

如何实现幂等

幂等没有一种专门的规则或者方法来实现,都是针对业务具体来做的。

幂等有多个可以讨论的维度。可以从大方向上按照操作(增删改查)来讨论。

  • 查天然是幂等的。
  • 按照唯一标识的删除也是天然幂等的。
  • 按照唯一标识的更新,如果不依赖于现有值,也是幂等的。
  • 新增操作、有条件的删除和更新,以及虽然按照唯一标识,但是依赖于现有值的更新都不是幂等的。

实现幂等有两个大方向:

  • 防重复;
  • 重复操作效果一致。

除了天然幂等的操作外,实现重复操作效果一致非常困难,因此一般幂等的解决思路就是防重复。防重复一般分为两步:

  • 生成唯一令牌
  • 验证唯一令牌

唯一令牌可以由客户端也可以由服务端生成。

  • 客户端生成的话,保证的就是同一客户端的某一个操作只会执行一次,这种情况针对的是客户端重复提交的(包括用户自己重复提交和客户端自己的超时重试)。比如客户端发送一个通知给服务端,客户端会生成一个唯一ID,第一次请求超时后,第二次再用这个ID去请求,服务端需要记录下已经执行的唯一ID,下次再来这个ID时,就不会再一次进行操作。
  • 服务端生成的话,一般是业务维度的标识,比如如果支付前需要先生成一笔交易,服务端可以将唯一的交易ID给客户端,客户端接下来的操作都是针对这个交易ID的。
  • 服务端如果不用业务标识的话,效果就和客户端生成类似,可以将客户端的操作分为两步:第一步获取一个唯一ID,第二步是实际操作,并在操作中将唯一ID放入。

具体到服务端验证唯一令牌而言,一般有以下方式:

  • 防重表,每次请求都去防重表中插入一条数据。如果重复就表示已经在处理了。有些文章所说的唯一索引其实也就是防重表的思路。
  • 分布式缓存,可以在 Redis 集群中使用一个 Set 来进行排重。

唯一令牌是一种比较通用的方式,如果对于特定的一些业务,它的事务本身有状态,也可以用状态机配合锁来进行幂等。即先用分布式锁或数据库锁来锁住状态,然后修改状态。这样后来的事务发现状态改变就直接返回。

幂等和并发导致的数据不一致

幂等和并发导致的数据不一致是不一样的,并发下的数据不一致解决的是两个同一个时间区间内进行的事务,如何不相互影响。比如有些文章说到的分布式锁和 CAS,其实解决的是并发的情况下数据不一致的问题。

不过 CAS 如果配合版本号的话,也可以起到幂等的效果。比如操作前就给了客户端一个版本号 v1,接下来扣减操作就是 update t set amount = amount-10 where account=a and version=v1。这个本质还是令牌。但这种使用方式很变扭,相当于一个很长的锁了。

对那种 update t set amount = amount-10 where account=a and amount=old_amount 这种,并不能保证查出的 old_amount 就是正确的值,可能已经是前一次操作后的结果了,没有任何意义。这个只能解决并发下数据不一致的问题。

多层幂等

幂等还有一个问题在于一个系统中需要多层幂等。什么意思呢?A 发送请求给 B,B 处理的一部分是要发送请求给另一个系统 C,C 在处理的过程中还可能需要发请求给另一个系统 D…… D 处理完了返回给 C,C 返回给 B,B 返回给 A。在这个链条中,如果 A B C D 中任何一个系统并没有正确实现幂等,也就是出现了 “幂等漏洞”,那么一个请求还是有可能被多次执行,产生区别于一次执行的影响。

参考

每个工程师都应该了解的:聊聊幂等

这篇关于聊聊幂等的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!