聊一聊幂等
幂等是什么?百度上给出的解释如下:
幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“setTrue()”函数就是一个幂等函数,无论多次执行,其结果都是一样的.更复杂的操作幂等保证是利用唯一交易号(流水号)实现。
1
编程中的幂等
概念
在我们日常开发和业务实现中,对于相同的参数输入,多次调用相同的功能,对资源的影响是一样的,也就是一次和多次请求某一个资源应该具有同样的副作用。
幂等解决的问题
常见的幂等场景
我们在研发工作中,需要考虑幂等的场景也比较多,常见的就是接口幂等、消息幂等和数据库幂等等。
2
接口幂等
接口幂等又常分为http接口幂等和RPC接口幂等。
HTTP接口幂等
我们常用的http接口对应的请求方式中:
其他请求方式我们平时基本很少用到,这里不再一一列举。
RPC接口幂等
RPC接口用于领域设计后的功能拆分,调用是跨进程的,对于RPC接口中的幂等,其实是对于外部调用超时重试,或者同样参数多次调用同一个接口,要保证对服务端资源的影响和一次调用是一样的,我们经常遇到的情况是状态机的变更,比如客户端调用RPC服务完结退款的状态,那么多次调用要保证和一次调用一样,退款状态只能被修改一次,并且最终的状态是完结。
3
消息幂等
对于消息幂等,可能大家对其了解不是很多,我们应用中基本都会使用消息队列,那么肯定会涉及到消息投递和消息接收,消息幂等也要分两个维度来分析:
消息投递幂等
所谓消息投递幂等,就是同一条消息只能被投递一次,对于消息broker来说,就算同一条消息投递多次,我也只存储一条。我们举个例子来说明一下:
问题就在于响应丢失重复投递,有可能消息broker已经成功接收消息并且存储了,重复投递的消息有可能被消息broker接收并存储,导致broker接收了两条或者两条以上的相同消息,也就会导致消息接收方接收到多条相同的消息,在业务场景中可能造成业务异常。
对于这种重复投递的消息,消息broker层可以对每一条消息生成一个唯一的code,有重复消息过来的时候,生成的code也会相同,如果发现相同就丢弃。
消息接收幂等
消息接收幂等,是消息broker中的同一条消息只能被consumer接收处理一次,就算broker推送多次,也只能消费一条。同样举个例子来说明一下:
消息broker重复推送相同消息的时候,有可能consumer已经接受成功并处理了业务,再次收到消息会导致consumer重复处理逻辑,假如在资金相关的场景出现这种情况,会导致重复出账造成资损。
对于消息broker重复推送的消息,consumer要对每一条消息生成唯一id或者code,如果发现重复消息,直接丢弃。
4
数据库幂等
所谓数据库幂等,也就是我们对DB层的操作幂等,说人话就是保证我们业务操作sql是幂等的,其实就是我们同一条sql执行多次和执行一次的效果是一样的,就拿CRUD来说,有些是幂等的,有些不是幂等但是可以通过调整转换成幂等的:
查询(Retrieve)
对于数据库查询,只是单穿的从数据库获取资源,不会更新数据,所以是幂等的。
创建(Create)
对于新增数据操作,很多时候是非幂等的,执行一次insert就会新增一条数据(id是自增主键):
insert into User(id,name) values(null,'typhoon');
这条sql是非幂等的,每执行一次都会新增一条记录,但是通过改造我们可以将其变成幂等的:
insert into User(id,name) values(100,'typhoon');
这样就变成幂等的了,不使用自增序列,通过程序生成主键,这样重复执行,数据库也只会新增一条数据。
更新(Update)
数据库更新操作,有些是幂等的,有些是非幂等的,典型的场景就是值递增,比如把小明的账户新增10000块:
update Account set money = money + 10000 where name = '小明';
这条sql是非幂等的,如果由于上层程序有bug,导致该sql重复被调用,那么就赶紧找地方哭去吧。我们通过简单的调整就可以把上述sql给造成幂等的:
var currentMoney = select money from Account where name = '小明'; var targetMoney = currentMoney + 10000; update Account set money = targetMoney where name = '小明' and money = currentMoney;
这样上述的非幂等sql就改造成了幂等了,有两个关键的点需要注意:
删除(Delete)
带精确匹配的删除sql是幂等的,其他的基本是非幂等的,典型的就是范围删除:
delete from Table where id > 1000;
这个sql是非幂等的,在删除数据的同时,有并发程序在新增数据,那么就导致每一次执行delete都删除了数据,也就违反了执行多次和一次对资源的副作用一样。经过改造把上述sql改成幂等的:
delete from Table where id in (?,?,?);
这样把范围删除变成了精确匹配删除,也就变成了幂等sql。
总结
幂等对我们应用架构非常重要,大公司把幂等当做一个应用编码规范,可见其重要程度,不管是网络超时重试、还是程序有bug导致的多次调用,幂等在很大程度上保护了我们的系统资源。特别是牵扯到资金的场景,任何一层的实现都要做幂等。
本文分享自 PersistentCoder 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!