详细讲解服务幂等性设计

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 在日常工作中的一些技术设计方案评审会上,经常会有提到注意服务接口的幂等性问题,最近就有个同学就跑到跟前问我,幂等性到底是个啥?

引子


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


在目前分布式/微服务化的今天,提供的服务能力丰富多样,基于 HTTP 协议的 Web API 是时下最为流行的一种分布式服务提供方式,对于服务的幂等性保障尤为重要。


我想了想,觉得有必要好好给大家普及一下才行。


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


1、何为幂等性?


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


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


微信图片_20220609111945.jpg


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


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


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


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


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


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


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


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


  • MQ 消息中间件,消息重复消费


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


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


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、如何解决幂等性问题?


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


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


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

微信图片_20220609111948.jpg

4.1 控制重复请求


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


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


主要解决方案:


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


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


4.2 过滤重复动作


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


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,这样就保证了业务代码不被重复执行。

微信图片_20220609111950.jpg


3)缓冲队列


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


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


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


微信图片_20220609111952.jpg

4.3 解决重复写


实现幂等性常见的方式有:悲观锁(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 位分割成多个部分,每个部分代表不同的含义。


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


5、总结


通常情况下,非幂等问题,主要是由于重复且不确定的写操作造成的。


1、解决重复的主要思考点


从请求全流程,控制重复请求触发以及重复数据处理


  • 客户端 控制发起重复请求


  • 服务端 过滤重复无效请求


  • 底层数据处理 避免重复写操作


2、控制不确定性主要思考点


从服务设计思路上做改变,尽量避免不确定性:


  • 统计变量改为数据记录方式


  • 范围操作改为确定操作


后记


听了我以上大段的讲述后,他好像收获感满满的似的说:大概理解了...


但是出于自身责任感,我还得叮嘱他几句:


1)幂等性处理 虽然复杂了业务处理,也可能会降低接口的执行效率,但是为了保证系统数据的准确性,是非常有必要的;


2)遇到问题,善于发现并挖掘本质问题,这样解决起来才能高效且精准;


3)选择自身业务场景适合的解决方案,而不要去硬套一些现成的技术实现,无论是组合还是创新,要记住适合的才是最好的。


愿大家能够掌握问题分析以及解决的能力,都不要一上来就急于解决问题,可以多做些深入分析,了解本质问题之后再考虑解决办法进行解决。


希望今天的讲解对大家有所帮助,Thanks for reading!

相关文章
|
SQL 缓存 NoSQL
接口的幂等性设计和防重保证,详细分析幂等性的几种实现方法
本篇文章详细说明了幂等性,解释了什么是幂等性,幂等性的使用场景,讨论了幂等和防重的概念。分析了幂等性的情况以及如何设计幂等性服务。阐述了幂等性实现防重的几种策略,包括乐关锁,防重表,分布式锁,token令牌以及支付缓冲区。
8345 0
接口的幂等性设计和防重保证,详细分析幂等性的几种实现方法
|
关系型数据库 MySQL 数据挖掘
MYSQL日期与时间函数的实用技巧
MYSQL日期函数与时间函数是数据库操作的关键工具,可轻松处理、查询、比较和格式化日期时间数据。它们能提取日期的年、月、日等部分,便于筛选和统计;同时,也能处理时间数据,如计算时间差、获取当前时间,助力用户更好地管理时间信息。掌握这些函数,不仅能提升数据库操作效率,还能为数据分析和报表生成提供有力支持。无论初学者还是资深数据库管理员,精通MYSQL的日期和时间函数都至关重要,以满足各种数据处理需求,确保数据的准确性和高效性。
709 0
|
消息中间件 SQL 存储
超详细的RabbitMQ入门,看这篇就够了!
RabbitMQ入门,看这篇就够了
217040 68
|
Linux Android开发
开源免费版RPA:详细指南[2.0版]
和市面上任何软件一样,全世界都在一起努力创建开源软件。那么RPA有开源的吗?答案是肯定的。但开源RPA生态系统目前尚不成熟。51RPA小编预测,这种情况不会在不久的将来发生变化,因为我们还没有看到支持无代码RPA的主要营利实体。
|
5月前
|
人工智能 算法 机器人
《探秘移动游戏的物理魔法:引擎应用与性能进阶指南》
物理引擎是移动游戏中实现真实感和趣味性的核心技术,通过模拟重力、碰撞、惯性等物理现象,增强沉浸感。在赛车游戏、解谜游戏等不同类型中,物理引擎发挥着独特作用,如《愤怒的小鸟》利用Box2D精准模拟碰撞效果。然而,移动设备性能有限,需优化物理模拟,包括控制物体数量、调整更新频率、简化模型等。未来,随着硬件升级和AI融合,物理引擎将实现更复杂的效果,如流体模拟和智能动态调整,同时工具的易用性提升也将助力开发者创造更精彩的游戏体验。
158 13
|
11月前
|
设计模式 缓存 前端开发
什么是幂等性?四种接口幂等性方案详解!
本文深入分布式系统中的幂等性问题及其解决方案,涵盖数据库唯一主键、乐观锁、PRG模式和防重Token等方法,关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
什么是幂等性?四种接口幂等性方案详解!
|
人工智能 编解码 5G
虚拟现实(VR)与增强现实(AR)的融合:开启全新交互时代
【6月更文挑战第17天】虚拟现实(VR)与增强现实(AR)融合成混合现实(MR),打造全新交互体验。MR结合VR的沉浸感和AR的现实增强,应用于教育、游戏、设计和营销,带来创新教学方式、沉浸式游戏体验和高效设计工具。尽管面临技术挑战,随着5G和AI的发展,MR有望引领未来交互的革命。
|
10月前
|
编解码 网络协议 机器人
顶顶通电话机器人开发接口对接大语言模型之实时流TTS对接介绍
大语言模型通常流式返回文字,若一次性TTS会导致严重延迟。通过标点断句或流TTS可实现低延迟的文本到语音转换。本文介绍了电话机器人接口适配流TTS的原理及技术点,包括FreeSWITCH通过WebSocket流TTS放音,以及推流协议和旁路流对接的详细说明。
740 1
|
前端开发 API 数据库
面试官问:如何防止重复提交请求,99%的前端能说出来!
如何防止接口重复提交是一个常见的系统设计问题,主要目的是确保关键操作的原子性和一致性。以下是简化的摘要: 这些方法可以单独或组合使用,取决于系统规模和业务需求。例如,对于低流量系统,简单的请求唯一ID和数据库唯一索引可能足够;而对于高并发场景,可能需要结合前端禁用和后端分布式锁来提高可靠性。幂等性设计是确保接口安全的一种通用策略,适用于各种场景。
|
存储 缓存 算法
IM技术干货:假如你来设计微信的群聊,你该怎么设计?
微信背后的这个IM群聊系统到底是如何实现的呢?这个问题一直困扰着,于是我决定深入了解一下,看看微信的群聊系统背后的设计是怎样的。
432 1