在软件系统的开发过程当中,咱们可能有以下需求:redis
等等不少状况下,都须要幂等的特性来支持。数据库
幂等(idempotence)一词原为数学上的概念,用一个最直观的数学式子表达为:缓存
f(f(x)) = f(x)
对应到软件开发领域,即为一样的请求被执行一次与连续执行屡次的效果是同样的,服务器的状态也是同样的,实际上就是接口的可重复调用
(包括时间和空间上两个维度)。
不是要求返回值彻底相同,并且是指后续多余的调用对系统的数据一致性不形成破坏。对于写入类操做,若是第一次写入是成功的,后续的写入应该抛出异常或者空操做,或者执行了写入可是未对数据形成变化。对于读取类操做,须要保证其实现上是真正的读取,不能在读操做中夹带写操做。服务器
幂等性不能脱离业务来讨论。
在不一样的需求场景下,实现幂等的思路和方案也会不一样,通常有以下通用方案:网络
这是乐观锁的一种实现,用于在数据库并发访问时的状况。当数据更新时须要去判断版本号是否一致,若是不一致,则没法对数据进行更新。例如请求支付接口进行扣款时:并发
update table_name set deposit = deposit-#{payment}, version = version + 1 where orderId = #{orderId} and version = #{version}
这里orderId可进一步设为主键或惟一索引,由于这样是行锁,不然更新操做时会锁表。乐观锁在对已有数据进行更新时既能保证效率,又能保证幂等。分布式
这是利用数据库表单的特性来实现幂等。以订单请求支付场景为例:
将订单号orderId设为去重表的惟一索引,每次请求支付都根据订单号向去重表中插入一条数据,只有插入成功才继续执行支付操做,至关于在事务的开始阶段加锁。
考虑两种失败的状况:ide
以上两种失败的状况下,事务的幂等性是能够保持的,避免了单个订单同时屡次进行支付的状况。
下图为该支付场景下的时序图:spa
与去重表思路相同,只是将对数据库的的访问转移到了对缓存(如redis)的访问,提升了效率。具体操做以下:
订单发起支付请求,支付系统会去redis缓存中查询是否存在该订单号orderId的key,若是不存在,则向redis增长key为订单号,而后开始实际支付操做;若是查询到存在该订单号的key,则不进行实际支付操做。不管支付操做成功或失败,在支付操做结果返回后,在缓存中删除该订单号key。code