我是这样给同事分析幂等性问题的

简介: 在日常一些技术设计方案评审会上,经常会提到注意服务接口的幂等性问题,最近有个同学就跑到跟前问我,到底啥是幂等性?今天就关于服务幂等性的一系列问题,在此将材料稍作整理,分享给大家~尤其在目前分布式/微服务化的今天,提供的后端服务接口,注意做好幂等性设计很有必要。

引子


在日常一些技术设计方案评审会上,经常会提到注意服务接口的幂等性问题,最近有个同学就跑到跟前问我,到底啥是幂等性?


今天就关于服务幂等性的一系列问题,在此将材料稍作整理,分享给大家~


尤其在目前分布式/微服务化的今天,提供的后端服务接口,注意做好幂等性设计很有必要


本文大纲


1、何为幂等性?


2、幂等性主要场景有哪些?


3、幂等性的作用是什么?


4、如何解决幂等性问题?


1、何为幂等性?


“幂等(idempotence),来源于数学中的一个概念,例如:幂等函数/幂等方法(指用相同的参数重复执行,并能获得相同结果的函数,这些函数不影响系统状态,也不用担心重复执行会对系统造成改变)。


简单理解即:多次调用对系统的产生的影响是一样的,即对资源的作用是一样的

微信图片_20220607132833.png

幂等性


幂等性强调的是外界通过接口对系统内部的影响, 只要一次或多次调用对某一个资源应该具有同样的副作用就行。


注意:这里指对资源造成的副作用必须是一样的,但是返回值允许不同!

 

2、幂等性主要场景有哪些?


根据上面对幂等性的定义我们得知:产生重复数据或数据不一致,这个绝大部分是由于发生了重复请求


这里的重复请求是指同一个请求在一些情况下被多次发起。


导致这个情况会有哪些场景呢?


1)微服务架构下,不同微服务间会有大量的基于http,rpc或者mq消息的网络通信,会有第三个情况【未知】,也就是超时。如果超时了,微服务框架会进行重试。


2)用户交互的时候多次点击,无意地触发多笔交易。


3)MQ消息中间件,消息重复消费


4)第三方平台的接口(如:支付成功回调接口),因为异常也会导致多次异步回调


5)其他中间件/应用服务根据自身的特性,也有可能进行重试。


3、幂等性的作用是什么?


幂等性主要保证多次调用对资源的影响是一致的


在阐述作用之前,我们利用资源处理应用来说明一下:


HTTP与数据库的CRUD操作对应:


  PUT :CREATE


  GET :READ


  POST :UPDATE


  DELETE :DELETE


(其实不光是数据库,任何数据如文件图表都是这样)


1)查询


SELECT * FROM users WHERE xxx;

不会对数据产生任何变化,天然具备幂等性。


2)新增


INSERT INTO users (user_id, name) VALUES (1, 'zhangsan');


case1:带有唯一索引(如:`user_id`),重复插入会导致后续执行失败,具有幂等性;


case2:不带有唯一索引,多次插入会导致数据重复,不具有幂等性。


3)修改


case1:直接赋值,不管执行多少次score都一样,具备幂等性。


UPDATE users SET score = 30 WHERE user_id = 1;


case2:计算赋值,每次操作score数据都不一样,不具备幂等性。


UPDATE users SET score = score + 30 WHERE user_id = 1;


4)删除


case1:绝对值删除,重复多次结果一样,具备幂等性。


DELETE FROM users WHERE id = 1;


case2:相对值删除,重复多次结果不一致,不具备幂等性。


DELETE top(3) FROM users;


总结:通常只需要对写请求(新增&更新)作幂等性保证

 

4、如何解决幂等性问题?


我们在网上搜索幂等性问题的解决方案,会有各种各样的解法,但是如何判断哪种解决方案对于自己的业务场景是最优解,这种情况下,就需要我们抓问题本质。


经过以上分析,我们得到了解决幂等性问题就是要控制对资源的写操作


我们从问题各个环节流程来分析解决:

微信图片_20220607132837.png

幂等性问题分析


01控制重复请求


控制动作触发源头,即前端做幂等性控制实现


相对不太可靠,没有从根本上解决问题,仅算作辅助解决方案。


主要解决方案:


  • 控制操作次数,例如:提交按钮仅可操作一次(提交动作后按钮置灰)


  • 及时重定向,例如:下单/支付成功后跳转到成功提示页面,这样消除了浏览器前进或后退造成的重复提交问题。

02过滤重复动作


控制过滤重复动作,是指在动作流转过程中控制有效请求数量。


1)分布式锁


利用Redis记录当前处理的业务标识,当检测到没有此任务在处理中,就进入处理,否则判为重复请求,可做过滤处理。


“订单发起支付请求,支付系统会去Redis缓存中查询是否存在该订单号的Key,如果不存在,则向Redis增加Key为订单号。查询订单支付已经支付,如果没有则进行支付,支付完成后删除该订单号的Key。通过Redis做到了分布式锁,只有这次订单订单支付请求完成,下次请求才能进来。


分布式锁相比去重表,将放并发做到了缓存中,较为高效。思路相同,同一时间只能完成一次支付请求。


2)token令牌


应用流程如下:


1)服务端提供了发送token的接口。执行业务前先去获取token,同时服务端会把token保存到redis中;


2)然后业务端发起业务请求时,把token一起携带过去,一般放在请求头部;


3)服务器判断token是否存在redis中,存在即第一次请求,可继续执行业务,执行业务完成后将token从redis中删除;


4)如果判断token不存在redis中,就表示是重复操作,直接返回重复标记给client,这样就保证了业务代码不被重复执行。

微信图片_20220607132839.png

token令牌处理流程图


3)缓冲队列


把所有请求都快速地接下来,对接入缓冲管道。后续使用异步任务处理管道中的数据,过滤掉重复的请求数据。


优点:同步转异步,实现高吞吐。


缺点:不能及时返回处理结果,需要后续监听处理结果的异步返回数据。

微信图片_20220607132842.png

缓存队列  


03解决重复写


实现幂等性常见的方式有:悲观锁(for update)、乐观锁、唯一约束。


1)悲观锁(Pessimistic Lock)


简单理解就是:假设每一次拿数据,都有认为会被修改,所以给数据库的行或表上锁。


当数据库执行select for update时会获取被select中的数据行的行锁,因此其他并发执行的select for update如果试图选中同一行则会发生排斥(需要等待行锁被释放),因此达到锁的效果。


select for update获取的行锁会在当前事务结束时自动释放,因此必须在事务中使用。(注意for update要用在索引上,不然会锁表)


START TRANSACTION; # 开启事务
SELETE * FROM users WHERE id=1 FOR UPDATE;
UPDATE users SET name= 'xiaoming' WHERE id = 1;
COMMIT; # 提交事务


2)乐观锁(Optimistic Lock)


简单理解就是:就是很乐观,每次去拿数据的时候都认为别人不会修改。更新时如果version变化了,更新不会成功。


不过,乐观锁存在失效的情况,就是常说的ABA问题,不过如果version版本一直是自增的就不会出现ABA的情况。


UPDATE users SET name='xiaoxiao', version=(version+1) WHERE id=1 AND version=version;


缺点:就是在操作业务前,需要先查询出当前的version版本


另外,还存在一种:状态机控制

例如:支付状态流转流程:待支付->支付中->已支付


具有一定要的前置要求的,严格来讲,也属于乐观锁的一种


3)唯一约束


常见的就是利用数据库唯一索引或者全局业务唯一标识(如:source+序列号等)。


这个机制是利用了数据库的主键唯一约束的特性,解决了在insert场景时幂等问题。但主键的要求不是自增的主键,这样就需要业务生成全局唯一的主键,


全局ID生成方案:


  • UUID:结合机器的网卡、当地时间、一个随记数来生成UUID;


  • 数据库自增ID:使用数据库的id自增策略,如 MySQL 的 auto_increment。


  • Redis实现:通过提供像 INCR 和 INCRBY 这样的自增原子命令,保证生成的 ID 肯定是唯一有序的。


  • 雪花算法-Snowflake:由Twitter开源的分布式ID生成算法,以划分命名空间的方式将 64-bit位分割成多个部分,每个部分代表不同的含义。

小结:按照应用上的最优收益,推荐排序为:乐观锁 > 唯一约束 > 悲观锁。


 

后记


听了我以上大段的讲述后,他好像收获感满满的似的说:理解了...但是出于自身责任感,我还得叮嘱他几句:1)幂等性处理 虽然复杂了业务处理,也可能会降低接口的执行效率,但是为了保证系统数据的准确性,是非常有必要的;2)遇到问题,善于发现并挖掘本质问题,这样解决起来才能高效且精准;3)选择自身业务场景适合的解决方案,而不要去硬套一些现成的技术实现,无论是组合还是创新,要记住适合的才是最好的。

相关文章
|
Linux 虚拟化
Linux命令(35)之shutdown
Linux命令(35)之shutdown
1198 1
|
机器学习/深度学习 人工智能 安全
《昇腾芯片:鸿蒙NEXT人工智能算力体系的核心驱动力》
在人工智能快速发展的背景下,鸿蒙NEXT操作系统与昇腾芯片的结合带来了重大变革。昇腾芯片凭借卓越的计算性能(如昇腾910的320 TFLOPS半精度算力),加速模型训练和推理,缩短训练时间,提升效率。它与鸿蒙NEXT深度融合,实现高效协同,支持多场景应用,从云端到终端提供强大算力,并通过星盾安全架构保障数据安全。这一组合为智能生态的发展奠定了坚实基础。
875 14
|
9月前
|
数据采集 JSON API
淘宝/天猫获取商品销量详情 API 返回值有什么作用?
本文档介绍了商品销量查询接口的典型返回值结构、核心字段解析、注意事项、代码示例及替代方案。内容涵盖JSON示例、数据含义、调用限制、Python调用示例、合规性要求等,适用于开发者和数据分析人员。
|
机器学习/深度学习 数据可视化 大数据
基于马尔可夫链的状态转换,用概率模型预测股市走势
本文探讨了马尔可夫链在股市分析中的应用,通过定义市场状态和构建转移矩阵,揭示短期波动与长期趋势的概率特征。模型基于“无记忆性”假设,量化状态转换概率,帮助评估风险、识别模式并制定策略。例如,计算稳态分布可预测市场长期平衡态。尽管模型简化了复杂动态,但仍为投资决策提供了数据支持。同时,文章强调其局限性,如外部冲击影响和状态定义主观性,建议结合其他工具综合分析。未来可探索与机器学习融合,提升市场理解深度。
1243 7
基于马尔可夫链的状态转换,用概率模型预测股市走势
|
10月前
|
存储 搜索推荐 算法
Java 大视界 -- Java 大数据在智慧文旅旅游线路规划与游客流量均衡调控中的应用实践(196)
本实践案例深入探讨了Java大数据技术在智慧文旅中的创新应用,聚焦旅游线路规划与游客流量调控难题。通过整合多源数据、构建用户画像、开发个性化推荐算法及流量预测模型,实现了旅游线路的精准推荐与流量的科学调控。在某旅游城市的落地实践中,游客满意度显著提升,景区流量分布更加均衡,充分展现了Java大数据技术在推动文旅产业智能化升级中的核心价值与广阔前景。
|
计算机视觉
RT-DETR改进策略【Neck】| GFPN 超越BiFPN 通过跳层连接和跨尺度连接改进RT-DETR颈部网络
RT-DETR改进策略【Neck】| GFPN 超越BiFPN 通过跳层连接和跨尺度连接改进RT-DETR颈部网络
582 12
RT-DETR改进策略【Neck】| GFPN 超越BiFPN 通过跳层连接和跨尺度连接改进RT-DETR颈部网络
|
搜索推荐 前端开发 数据安全/隐私保护
改善用户体验方法
【10月更文挑战第9天】改善用户体验方法
1344 3
|
机器学习/深度学习 自动驾驶 算法
深度学习之因果推理与决策
基于深度学习的因果推理与决策是一个将因果推理理论与深度学习技术结合,旨在从数据中学习因果关系并基于此做出最优决策的领域。因果推理不仅关注变量之间的相关性,还侧重于发现变量之间的因果关系,而这些因果关系是决策系统做出有效决策的关键。
643 1
|
数据挖掘 Python
【Python数据分析】假设检验的基本思想、原理和步骤
文章详细介绍了假设检验的基本思想、原理、可能犯的错误类型、基本步骤以及在不同总体情况下的检验方法,阐述了如何在Python中应用假设检验,并通过P值来判断假设的可靠性。
561 1
|
C++ 网络协议 Shell
LVS-DR之VIP、DIP跨网段实例
在日常应用环境中,我们会遇到这样一种lvs部署环境,所有的dr以及的rs server都在一个局域网环境中,但只有一个公网ip,而又需要将应用发布到internet上,都知道lvs的最好的模式就是所有的server都有一个公网ip,但很多时候公网资源稀缺,当出现只有一个公网ip的时候,怎么实现l...
2240 0

热门文章

最新文章