tag 网站备案,北京建网站的价格,wordpress微官网,中国建设很行河北省分行合作网站什么是幂等性#xff1f;
所谓幂等性#xff0c;就是一次操作和多次操作同一个资源#xff0c;所产生的影响均与一次操作的影响相同。
幂等#xff08;idempotent、idempotence#xff09;是一个数学与计算机学概念#xff0c;常见于抽象代数中。
幂等函数…什么是幂等性
所谓幂等性就是一次操作和多次操作同一个资源所产生的影响均与一次操作的影响相同。
幂等idempotent、idempotence是一个数学与计算机学概念常见于抽象代数中。
幂等函数或幂等方法是指可以使用相同参数重复执行并能获得相同结果的函数。
幂等性用数学语言表达就是
f(x)f(f(x))
维基百科的幂等性定义如下 幂等idempotent、idempotence是一个数学与计算机学概念常见于抽象代数中。 幂等函数或幂等方法是指可以使用相同参数重复执行并能获得相同结果的函数。 这些函数不会影响系统状态也不用担心重复执行会对系统造成改变。 例如“setTrue()”函数就是一个幂等函数,无论多次执行其结果都是一样的更复杂的操作幂等保证是利用唯一交易号(流水号)实现. 在软件或者系统中重复使用幂等函数或幂等方法不会影响系统状态也不用担心重复执行会对系统造成改变。
通俗点说
一个接口如果幂等不管被调多少次只要参数不变结果也不变。
幂等性是对于写操作来说的一个写操作一般都需要保证 幂等性 可用性 ACID事务属性。
当然这里仅仅聚焦 幂等。
为什么需要幂等性
如果客户端重复调用服务端会遇到如下的很多问题 创建订单时重复调用是否产生两笔订单 扣减库存时重复调是否会多扣一次
这就是出现了幂等性问题。
按照幂等性要求需要保证一次请求和多次请求同一个资源产生相同的副作用。
所以创建订单时重复调用是否产生两笔订单当然不能。
所以扣减库存时重复调是否会多扣一次当然不能。
这些都是需要幂等性机制去保障。如果不支持幂等操作那将会出现以下情况 电商超卖现象 重复转账、扣款或付款 重复增加金币、积分或优惠券
等等非常惨的。
什么样的原因导致幂等性问题
原因之一底层网络阻塞和延迟的问题
在系统高并发的环境下很有可能因为网络阻塞等等问题导致客户端不能及时的收到服务端响应甚至是调用超时。这时候用户会重复点击重复请求。
在消息队列组件中客户端也有重试机制如果投递失败/投递超时则会重新投递。对于服务端来说可能会收到重复投递的一份消息。
在RPC组件中客户端也有重试机制如果投递失败/投递超时则会重试调用。对于服务端来说可能会重复收到通用的调用。
原因之二用户层面的重复操作
比如下单的按键在点按之后在没有收到服务器请求之前用户还可以被按。
或者用户的App闪退/人工强退之后重新打开重新下单
需要幂等性的 两大场景
可能会发生重复请求或重试操作的场景在分布式、微服务架构中是随处可见的。 网络波动因网络波动可能会引起重复请求 分布式消息消费任务发布后使用分布式消息服务来进行消费 用户重复操作用户在使用产品时可能无意地触发多笔交易甚至没有响应而有意触发多笔交易 未关闭的重试机制因开发人员、测试人员或运维人员没有检查出来而开启的重试机制如Nginx重试、RPC通信重试或业务层重试等
大致可以分为两大类 第一类单数据CRUD操作的幂等性保证方案 第二类多数据并发操作的幂等性保证方案
第一类单数据CRUD操作的幂等性保证方案
首先来看看单数据CRUD操作的幂等性保证方案
对于单数据CRUD操作很多具备天然幂等性 新增类动作不具备幂等性 查询类动作重复查询不会产生或变更新的数据查询具有天然幂等性 更新类动作 基于主键的计算式Update不具备幂等性即UPDATE goods SET numbernumber-1 WHERE id1 基于主键的非计算式Update具备幂等性即UPDATE goods SET numbernewNumber WHERE id1 基于条件查询的Update不一定具备幂等性需要根据实际情况进行分析判断 删除类动作 基于主键的Delete具备幂等性 一般业务层面都是逻辑删除即update操作而基于主键的逻辑删除操作也是具有幂等性的
大家看到对于单数据CRUD操作 只有在下面的三个场景保证幂等即可 主键的计算式Update 基于条件查询的Update 新增类动作 第二类多数据并发操作的幂等性保证方案
大部分都是这种场景。
现在的应用大部分都是微服务的。并且一个操作会涉及到多个数据的并发操作会通过RPC调用到多个微服务。
分为两种情况 多数据同步操作一般是服务端提供一个统一的同步操作api客户端调用该api完成直接获得操作结果。 多数据异步操作由于同步操作性能低在高并发场景都会同步变异步于是乎服务端还要额外提供一个查询操作结果的api去查询结果。第一次超时之后调用方调用查询接口如果查到了就走成功的流程失败了就走失败的流程。
多数据并发操作的经典场景参考如下
1. 高并发抢红包
在抢一份红包的时候点击了抢开始异步抢红包。
抢到就有没抢到就没有。
抢完之后无论我们重复点击多少次红包都会提示你已经抢过该红包了。
2. 高并发下单
高并发下单的一个很基本的问题就是要避免重复订单。
如果用户操作一次由于超时重试等原因一看下了两个单甚至10个重复单。
用户不晕倒在厕所才怪。
3. 高并发支付
在支付场景支付平台会生成唯一的支付连接不会再次生成另外的支付连接。
如何保证幂等呢
幂等性的的确保方案非常多大致如下图所示 一些基础性的幂等性解决方案
全局唯一ID
如果使用全局唯一ID就是根据业务的操作和内容生成一个全局ID在执行操作前先根据这个全局唯一ID是否存在来判断这个操作是否已经执行。
如果不存在则把全局ID存储到存储系统中比如数据库、Redis等。如果存在则表示该方法已经执行。
使用全局唯一ID是一个通用方案可以支持插入、更新、删除业务操作。
一般情况下对分布式的全局唯一id可以参考以下几种方式 UUID Snowflake 数据库自增ID 业务本身的唯一约束 业务字段时间戳拼接
唯一索引(去重表)
这种方法适用于在业务中有唯一标识的插入场景中比如在以上的支付场景中如果一个订单只会支付一次所以订单ID可以作为唯一标识。
这时我们就可以建一张去重表并且把唯一标识作为唯一索引在我们实现时把创建支付单据写入去重表放在一个事务中如果重复创建数据库会抛出唯一约束异常操作就会回滚。
插入或更新upsert
这种方法插入并且有唯一索引的情况比如我们要关联商品品类其中商品的ID和品类的ID可以构成唯一索引并且在数据表中也增加了唯一索引。这时就可以使用InsertOrUpdate操作。
多版本控制
这种方法适合在更新的场景中比如我们要更新商品的名字这时我们就可以在更新的接口中增加一个版本号来做幂等
boolean updateGoodsName(int id,String newName,int version);在实现时可以如下
update goods set name#{newName},version#{version} where id#{id} and version${version}状态机控制
这种方法适合在有状态机流转的情况下比如就会订单的创建和付款订单的付款肯定是在之前这时我们可以通过在设计状态字段时使用int类型并且通过值类型的大小来做幂等比如订单的创建为0付款成功为100付款失败为99。
在做状态机更新时我们就可以这样控制
update goods_order set status#{status} where id#{id} and status#{status}以上就是保证接口幂等性的一些方法。
综合性的解决方案一锁二判三更新
前面的方案都是一些基础性的方案。
在实际的业务中一般会结合起来使用。
在双11和双12的活动中对于幂等性问题支付宝团队摸索出来了一个综合性的解决方案一锁二判三更新。这个方案可以作为一个比较通用的综合性的幂等解决方案。
何为“一锁二判三更新”
简单来说就是当任何一个并发请求过来的时候 1. 先锁定单据 2. 然后判断单据状态是否之前已经更新过对应状态了 3.1 如果之前并没有更新则本次请求可以更新并完成相关业务逻辑。 3.2 如果之前已经有更新则本次不能更新也不能完成业务逻辑。 一锁、二判、三更性的核心步骤
第一步先加锁。
高并发场景建议是redis分布式锁而不是低性能的DB锁也不是CP型的 Zookeeper锁。
如果普通的redis分布式锁性能太低该如何
还可以考虑引入 锁的分段机制 比如内部分成100端总体上就大概能线性提升 100倍。
第二步进行幂等性判断。
幂等性判断就是 进行 数据检查。
可以基于状态机、流水表、唯一性索引等等前面介绍的 基础方案进行重复操作的判断。
第三步数据更新
如果通过了第二步的幂等性判断 说明之前没有执行过更新操作。
那么就进入第三步进行数据的更新将数据进行持久化。
操作完成之后 记得释放锁 结束整个流程。