分布式架构之详解幂等

news2025/1/14 18:44:25

幂等的概念

在数学里,幂等有两种主要的定义。
1、在某二元运算下,幂等元素是指被自己重复运算(或对于函数是为复合)的结果等于它自己的元素。例如,乘法下仅有两个幂等实数,为0和1。
2、某一元运算为幂等的时,其作用在任一元素两次后会和其作用一次的结果相同。例如,高斯符号便是幂等的。
一元运算的定义是二元运算定义的特例。

在计算机领域,幂等是指多次调用对系统产生的影响是一样的,即对资源的作用是一样的,但是返回值允许不同。

我们说一个 HTTP 方法是幂等的,指的是同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的。换句话说就是,幂等方法不应该具有副作用(统计用途除外)。在正确实现的条件下, GET , HEAD , PUT 和 DELETE 等方法都是幂等的,而 POST 方法不是。所有的 safe 方法也都是幂等的。
幂等性只与后端服务器的实际状态有关,而每一次请求接收到的状态码不一定相同。例如,第一次调用 DELETE 方法有可能返回 200 ,但是后续的请求可能会返回 404 。

举例来说,一个GET请求,每次返回的结果都相同,那么多次请求效果是相同的,我们就说这个请求是幂等的。而如果一个POST请求,每次请求后台都会新增一条记录,我们就说这个请求是不幂等的。

多次幂等请求:

GET /pageX HTTP/1.1   -> Return pageX
GET /pageX HTTP/1.1   -> Return pageX
GET /pageX HTTP/1.1   -> Return pageX

多次不幂等的请求:

POST /add_row HTTP/1.1   -> Adds a new row
POST /add_row HTTP/1.1   -> Adds a new row
POST /add_row HTTP/1.1   -> Adds a new row

对于SQL语句,如果多次执行产生的效果相同,我们就说这条SQL是幂等的,否则就不幂等。
举例来说,如果下面SQL中的name是有唯一索引的,多次执行以后数据库只能插入一条记录,那么这条SQL就是幂等的,否则不幂等。

INSERT INTO `table_name` (`name`) VALUES ('Sam');

在计算机中编程中,一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。

幂等设计

幂等性原本是数学上的概念,指的是使用相同的参数执行同一个函数时,无论执行多少次,都能输出相同的结果。在计算机编程中,幂等性指的是对于同一个方法来说,只要参数相同,无论执行多少次都与第一次执行时产生的影响相同。

在分布式系统中,为了保证一致性,业务服务对外提供操作业务数据的接口时,需要在接口的实现中保证对数据处理的幂等性。

但是在分布式环境中,难免会因为各种原因出现数据不一致的情况,此时,为了保证数据的最终一致性,系统会提供很多重试操作,保证最终的一致性。

如果这些重试操作涉及的方法中,某些方法的实现不具有幂等性,则即使重试操作成功了,也无法保证数据最终一致性。

通常有两种实现幂等性的方式:一种是通过业务操作本身实现幂等性;另一种是通过系统缓存所有的请求与处理结果,当再次检测到相同的请求时,直接返回之前缓存的处理结果。

幂等设计一般有两种处理方法:
(1)需要下游系统提供相关的查询接口。
上游系统第一次调用出现异常后,需要先调用下游系统提供的查询接口,如果查询到数据,表明上次的调用已经成功,就不需要做了,失败了就走失败流程。
(2)通过幂等性的方式。
也就是这个查询操作交给下游系统,上游系统只管重试,由下游系统保证一次和多次的请求结果是一样的。
幂等的解决方案非常多,需要根据具体的业务场景选择具体策略。

幂等方案一:数据库唯一主键实现幂等性

利用数据库中主键唯一约束的特性,保证一张表中只存在一条带该唯一主键的记录。
如果不是主键,也可以使用唯一索引保证数据库操作的幂等性,如果在分布式环境下使用唯一标识字段,可以使用分布式ID(可以使用Snowflake算法)充当主键,这样才能保证在分布式环境下ID的全局唯一性。

幂等方案二:数据库乐观锁实现幂等性

数据库乐观锁方案适用于执行更新操作,可以提前在对应的表中添加一个version字段,充当当前数据的版本标识。这样每次对表中的这条数据执行更新时,都会将该版本标识作为一个条件,值为待更新数据中的版本标识的值。

update table_name set name='NewName' where name='OldName' and version=1

幂等方案三:防重Token令牌实现幂等性

针对客户端连续点击或者调用方的超时重试等情况,例如提交表单,可以用Token机制实现防止重复提交。
简单地说,就是调用方在调用接口的时候,先向后端请求一个全局ID(Token),请求的时候携带这个全局ID一起请求(Token最好将其放到Headers中)。
后端需要将这个Token作为Key,用户信息作为Value,到Redis中进行键值内容校验,如果Key存在且Value匹配就执行删除命令,然后正常执行后面的业务逻辑,如果不存在对应的Key或Value不匹配就返回重复执行的错误信息,这样来保证幂等操作。
在这里插入图片描述

具体步骤如下:
步骤01:
 客户端通过Token服务获取Token令牌(序列号/分布式ID/UUID字符串)。
步骤02:
 Token服务将Token存入Redis缓存中,以该Token作为Redis的键(注意设置过期时间)。
步骤03:
 将Token返回到客户端,客户端拿到后保存到表单隐藏域中。
步骤04:
 客户端在执行提交表单时,把Token存到Headers中。
步骤05:
 服务端接收到请求后,从请求头Headers中拿到Token,根据Token到Redis中查找该key是否存在。
步骤06:
 服务端根据Redis中是否存在该key进行判断,如果存在就将该key删除,然后正常执行业务逻辑。如果不存在就抛出异常,返回重复提交的错误信息。

Token删除的时机有两种不同的处理方法:
(1)检验Token存在(表示第一次请求)后,就立刻删除Token,再进行业务处理。
先删除Token,这时如果业务处理出现异常,接口调用方也没有获取到明确的结果,就进行重试,但Token已经删除掉了,服务端判断Token不存在,认为是重复请求,因此直接返回,无法进行业务处理。
(2)检验Token存在(表示第一次请求)后,先进行业务处理,再删除Token。
后删除Token也是存在问题的,如果进行业务处理成功后,删除Redis中的Token失败,这样有可能导致发生重复请求,因为Token没有被删除。
综上所述,推荐先删除Token,先保证不会因为重复请求导致业务数据出现问题,最多让用户再请求处理一次。

另外,Token幂等方案还有一个问题,业务每次请求都会有额外的请求(获取Token请求、判断Token是否存在等)。
在生产环境中,1000个请求也许只会存在20个左右的请求会发生重试,为了这20个请求,让980个请求都发生额外的请求,显然有点浪费。

幂等方案四:分布式锁

分布式锁的实现方式可以基于Redis的SETNX命令实现。
SETNX命令的语法如下:

SETNX key value

将key的值设为value,当且仅当key不存在时,命令返回1。若给定的key已经存在,SETNX不做任何动作,命令返回0。
key可以取业务某个唯一字段的值,例如订单数据可以取订单ID,用户数据可以取用户ID,等等。

幂等方案五:去重表机制

去重表也叫幂等表,使用去重表方案需要业务中有唯一主键,去重表中只需要一个字段即可,用于设置唯一主键约束,当然也可以根据业务情况自行添加其他字段。
去重表机制的主要流程:
把唯一主键插入去重表,再进行业务操作,且它们处于同一个事务中。
当重复请求时,因为去重表有唯一约束,导致请求失败,可以避免幂等问题去重表和业务表应该在同一个库中,这样就保证了在同一个事务中,即使业务操作失败,也会把去重表的数据回滚。
这样可以很好地保证数据的一致性。
该方案也是比较常用的,去重表跟业务无关,很多业务可以共用同一个去重表,只要规划好唯一主键即可。

幂等方案六:状态机

在有状态的数据中可以使用,如果状态机已经处于下一个状态,这时候来了一个上一个状态的变更,理论上是不能够变更的,这样就保证了有限状态机的幂等。

例如,状态status只能进行1->2->3->4的顺序变更。
更新SQL可以写成:

update table_name set status=2 where id=1 and status=1
update table_name set status=3 where id=1 and status=2
update table_name set status=4 where id=1 and status=3

参考资料

  • 百度百科:幂等 https://baike.baidu.com/item/%E5%B9%82%E7%AD%89
  • https://developer.mozilla.org/zh-CN/docs/Glossary/Idempotent
  • https://www.21ic.com/article/883663.html
  • 《分布式高可用架构之道》
  • 《分布式应用系统架构设计与实践》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/365529.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

醒醒吧,外包测试哪有前途,你只是一块干电池而已,随时会被替换掉

我25岁的时候,外包测试,薪资13.5k,人在深圳。 内卷什么的就不说了,而且人在外包那些高级精英年薪大几十的咱也接触不到,就说说外包吧。假设以我为界限,25岁一线城市13.5k,那22-24大部分情况下是…

【项目】Vue3+TS 退出登录 menu header搭建

💭💭 ✨:【项目】Vue3TS 退出登录 menu header搭建   💟:东非不开森的主页   💜: 今天永远比昨天更好💜💜   🌸: 如有错误或不足之处,希望可以指正&#x…

Cesium集成WebXR_连接VR设备

Cesium集成WebXR 文章目录Cesium集成WebXR1. 需求2. 技术基础2.1 WebGL2.2 WebXR2.3 其他3. 示例代码4. 效果图5. 参考链接1. 需求 通过WebXR接口,将浏览器端连接到VR头盔,实现在VR头盔中浏览Cesium场景,并可将头盔旋转的操作同步映射为场景…

干测试5年,经常被开发看不起,现在总算证明了自己····

测试不止是点点点 我感觉我是一个比较有发言权的人吧,我在测试行业摸爬滚打5年,以前经常听到开发对我说,天天的点点点有意思没? 和IT圈外的同学、朋友聊起自己的工作,往往一说自己是测试,无形中也会被大家…

CVE-2022-39197 POC(CobaltStrike XSS <=4.7)漏洞复现

漏洞说明 根据9.20日CobaltStrike官方发布的最新4.7.1版本的更新日志中介绍&#xff0c;<4.7的teamserver版本存在XSS漏洞&#xff0c;从而可以造成RCE远程代码执行 一位名为“Beichendream”的独立研究人员联系我们&#xff0c;告知我们他们在团队服务器中发现的一个 XSS …

ABBYY FineReader16最新PDF图片文字识别软件

ABBYY FineReader16是非常好的一款 OCR 识别软件&#xff08;可以识别不可编辑的PDF和图片文件&#xff09;&#xff0c;操作非常简单。ABBYY FineReader 16是一款知名的OCR文字识别软件&#xff08;图片文字识别&#xff09;。ABBYY 16采用了ABBYY最新推出的基于AI的OCR技术&a…

JVM记录

一、JVM体系结构&#xff1a; 类装载器ClassLoader&#xff1a;用来装载.class文件执行引擎&#xff1a;执行字节码&#xff0c;或者执行本地方法运行时数据区&#xff1a;方法区、堆、Java栈、程序计数器、本地方法栈1、方法区&#xff1a; 也称“永久代”&#xff0c;“非堆”…

渗透测试之主机探测存活性实验

渗透测试之主机探测存活性实验实验目的一、实验原理1.1 TCP/IP协议1. TCP2. IP1.2 Ping的原理二、实验环境2.1 操作机器2.2 实验工具三、实验步骤1. 学会使用ping命令2. 使用Nmap进行多种方式的探测总结实验目的 熟悉TCP/IP协议、Ping命令基本概念学习nmap、SuperScan扫描方式…

【Database-02】达梦数据库 - DM Manager管理工具安装

1、简介 DM Manager是达梦数据库自带的图形化界面管理工具&#xff0c;在安装达梦数据库的时候就会自动安装。 Linux环境&#xff0c;默认安装路径为&#xff1a;达梦安装目录/tool/manager&#xff0c;如果Linux是安装GUI&#xff0c;那么就可以直接启动使用。 实际大部分使…

Python自动化测试框架封装和调用

封装与调用函数与参数化前言 面实现了参数的关联&#xff0c;那种只是记流水账的完成功能&#xff0c;不便于维护&#xff0c;也没什么可读性&#xff0c;接下来这篇可以把每一个动作写成一个函数&#xff0c;这样更方便了。参数化的思维只需记住一点&#xff1a;不要写死 登录…

[黑马程序员SSM框架教程]04 IOC-入门案例

1.IOC入门案例思路分析 管什么?管bean&#xff08;service和dao&#xff09;如何将被管理的对象告知IOC容器?&#xff08;配置&#xff09;被管理的对象交给IOC容器&#xff0c;如何获取到IOC容器&#xff1f;(提供了个接口)如何获取到IOC中的bean&#xff1f;&#xff08;接…

Kubernetes Nginx 发布

kubernetes发布nginx 目录 Nginx Pod启动Service访问Nginx 2.1. NodePort访问Nginx 2.2. ClusterIP访问Nginx 2.3. LoadBalancer访问Nginx 2.4. ExternalName访问NginxDeployment方式部署Nginx 3.1 Nginx Replicas Nginx Pod 启动 nginx-v1.yaml apiVersion: v1 kind: Pod…

基于SPI的增强式插件框架设计

很久之前&#xff0c;为了诊断线上的问题&#xff0c;就想要是能有工具可以在线上出问题的时候&#xff0c;放个诊断包进去马上生效&#xff0c;就能看到线上问题的所在&#xff0c;那该是多么舒服的事情。后来慢慢的切换到 java 领域后&#xff0c;这种理想也变成了现实&#…

Maven中开源的报表平台组件(自带页面+直连数据库)

借鉴的网址&#xff1a;https://www.w3cschool.cn/ureport/

一文搞懂秒杀系统,欢迎参与开源,提交PR,提高竞争力。早日上岸,升职加薪。

前言 秒杀和高并发是面试的高频考点&#xff0c;也是我们做电商项目必知必会的场景。欢迎大家参与我们的开源项目&#xff0c;提交PR&#xff0c;提高竞争力。早日上岸&#xff0c;升职加薪。 知识点详解 秒杀系统架构图 秒杀流程图 秒杀系统设计 这篇文章一万多字&#xff0c;…

PDMS二次开发(一)——PML类型程序类型与概念

目录前言一、PML类型与概念基础知识变量函数小例子注释PML表达式条件判断语句循环skip和break窗口程序在PDMS菜单栏中添加程序窗口自动定位PML常见控件前言 PDMS二次开发需要.net 有自带的PML语言和C# .net一般通常泛指的是C#语言 模型数据借助.NET的接口可以转换成数据库中的…

达梦8数据守护动态增加实时备库

实时主备环境 类型 业务IP 库名 实例名 PORT_NUM MAL_HOST MAL_INST_DW_PORT MAL_PORT MAL_DW_PORT 主库dm8p 192.168.1.223 DAMENG GRP1_RT_01 5236 10.0.0.223 45101 55101 65101 备库dm8s 192.168.1.224 DAMENG GRP1_RT_02 5236 10.0.0.224 45121…

模拟电路知识点总结(详细版)-- PN结

一、半导体&#xff1a;介于绝缘体和导体之间 二、本征半导体&#xff1a;纯净的半导体 1.晶体结构&#xff1a;正四面体 2.载流子&#xff1a; 本征激发:逃离共价键的束缚&#xff0c;成为自由电子 (本征半导体的本征激发&#xff0c;通常是由温度引起的晶体结构内部的共价键断…

免费基于springboot的OA自动化办公系统,挺漂亮的

大家好&#xff0c;我是锋哥&#xff0c;看到一个不错的springboot的OA自动化办公系统&#xff0c;分享下哈。 项目介绍 这是一个OA办公自动化系统&#xff0c;使用Maven进行项目管理&#xff0c;基于springboot框架开发的项目&#xff0c;mysql底层数据库&#xff0c;前端采…

GEE学习笔记 五十五:GEE编辑器绘制样本点的一个bug(官方在5.1给出反馈已经修复相关bug)

在做地物分类的时候我们会采用GEE在线采集样本方式&#xff0c;但是这个有一个问题需要注意&#xff0c;如果直接使用绘制矩形和点会将点变为 ee.Geometry.Point([xxx], null, false) 这种形式。出现的问题步骤如下&#xff1a; 1、绘制一个点和一个矩形 2、修改geometry为fea…