什么是幂等性?如何解决幂等性问题?

news2024/11/23 10:41:49

一、什么是幂等性?

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

通俗来说:就是多次调用对系统的产生的影响是一样的,即对资源的作用是一样的。

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

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

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

从上面幂等性的定义可知:产生重复数据或数据不一致,这个绝大部分是由于发生了重复请求。
这里的重复请求是指同一个请求在一些情况下被多次发起。那么,导致这个情况会有哪些场景呢?

  • 微服务架构下,不同微服务间会有大量的基于 http,rpc 或者 mq 消息的网络通信,会有第三个情况【未知】,也就是超时。如果超时了,微服务框架会进行重试。
  • 用户交互的时候多次点击,无意地触发多笔交易。
  • MQ 消息中间件,消息重复消费
  • 第三方平台的接口(如:支付成功回调接口),因为异常也会导致多次异步回调
  • 其他中间件/应用服务根据自身的特性,也有可能进行重试。

三、幂等性的作用?

幂等性主要保证多次调用对资源的影响是一致的。在阐述作用之前,我们利用资源处理应用来说明一下:

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.1 控制重复请求

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

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

主要解决方案:

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

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

4.2 过滤重复请求

控制过滤重复动作,是指在动作流转过程中控制有效请求数量。
1)分布式锁
利用 Redis 记录当前处理的业务标识,当检测到没有此任务在处理中,就进入处理,否则判为重复请求,可做过滤处理。
关于具体实现可以参考我的另一篇文章: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,这样就保证了业务代码不被重复执行。
在这里插入图片描述

3)缓冲队列
把所有请求都快速地接下来,对接入缓冲管道。后续使用异步任务处理管道中的数据,过滤掉重复的请求数据。
优点:同步转异步,实现高吞吐。
缺点:不能及时返回处理结果,需要后续监听处理结果的异步返回数据。
在这里插入图片描述

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 位分割成多个部分,每个部分代表不同的含义。

使用数据库唯一索引的场景:(插入数据时需要保证幂等性的场景)

  • 订单创建
  • 用户注册

小结:

选择哪种方法来解决幂等问题,需要根据具体的业务场景来进行权衡。

  • 乐观锁的优点是并发性能好,缺点是无法保证数据的强一致性。
  • 悲观锁的优点是可以保证数据的强一致性,缺点是并发性能差。
  • 数据库唯一索引可以保证数据的唯一性,但无法解决并发冲突问题。

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

五、总结

通常情况下,非幂等问题,主要是由于重复且不确定的写操作造成的。
1、解决重复的主要思考点
从请求全流程,控制重复请求触发以及重复数据处理

  • 客户端 控制发起重复请求
  • 服务端 过滤重复无效请求
  • 底层数据处理 避免重复写操作

2、控制不确定性主要思考点
从服务设计思路上做改变,尽量避免不确定性:

  • 统计变量改为数据记录方式
  • 范围操作改为确定操作

温馨提示:

  1. 幂等性处理 虽然复杂了业务处理,也可能会降低接口的执行效率,但是为了保证系统数据的准确性,是非常有必要的;
  2. 遇到问题,善于发现并挖掘本质问题,这样解决起来才能高效且精准;
  3. 选择自身业务场景适合的解决方案,而不要去硬套一些现成的技术实现,无论是组合还是创新,要记住适合的才是最好的。

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

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

相关文章

烂笔头笔记:Windows 11下照片查看器显示偏色问题修复

本文出处:http://blog.csdn.net/chaijunkun/article/details/137278931,转载请注明。由于本人不定期会整理相关博文,会对相应内容作出完善。因此强烈建议在原始出处查看此文。 最近在研究HDR视频的截图算法,目的就是生成色彩正确…

pytest--python的一种测试框架--接口测试

接口测试 工具: POSTMAN; 接口选择: 豆瓣电影,进制数据 POSTMAN下载: 1.POSTMAN官网:https://www.postman.com/products/; 2.点product选Download Postman 下载完之后双击打开就可以用的。…

MVCC的实现原理

简介 MVCC(Multi-Version Concurrency Control)即多版本并发控制。 MVCC的实现原理 我们在了解MVCC之前,首先先了解一下几个比较常见的锁。 **读锁:**也叫共享锁、S锁,若事务T对数据对象A加上S锁,则事务…

HarmonyOS应用开发ArkUI(TS)电商项目实战

项目介绍 本项目基于 HarmonyOS 的ArkUI框架TS扩展的声明式开发范式,关于语法和概念直接看官网官方文档地址:基于TS扩展的声明式开发范式, 工具版本: DevEco Studio 3.1 Canary1 SDK版本: 3.1.9.7(API V…

说说你对数据结构-树的理解

对树 - 二叉搜索树的理解 二叉搜索树是一种常见的二叉树结构,它具有以下特点: 每个节点最多只有两个子节点,分别称为左子节点和右子节点;对于任意节点,其左子树中的所有节点均小于该节点,其右子树中的所有…

linux 回收站机制(笔记)

Linux下回收站机制https://mp.weixin.qq.com/s/H5Y8VRcaOhFZFXzR8yQ7yg 功能 :设立回收站,并且可定时清空回收站。 一、建议将alias rm 改成别的。 比如alias rmm ,同时修改rm -rf ~/.trash/* 改成 rmm -rf ~/.trash/* 不然影响rm 的正常使…

JUC:synchronized优化——锁的升级过程(偏向锁->轻量级锁->重量级锁)以及内部实现原理

文章目录 锁的类型轻量级锁重量级锁自旋优化偏向锁偏向锁的细节偏向锁的撤销批量重偏向批量撤销锁消除 锁的类型 重量级锁、轻量级锁、偏向锁。 加锁过程:偏向->轻量级->重量级 轻量级锁 轻量级锁的使用场景:如果一个对象虽然有多线程要加锁&am…

OpenHarmony Neptune开发板-MQTT连接华为IoT平台

本示例将演示如何在Neptune开发板上使用MQTT协议连接华为IoT平台,使用的是ATH20温湿度传感器模块与Neptune开发板 本示例实现AHT20温湿度数据上报华为IoT平台,IoT平台下发命令控制LED灯的开关 使用W800 SDK功能包中libemqtt来实现连接华为IoT平台 程序设计 初始化 一、MQT…

k8s 基础入门

1.namespace k8s中的namespace和docker中namespace是两码事,可以理解为k8s中的namespace是为了多租户,dockers中的namespace是为了网络、资源等隔离 2.deployment kubectl create #新建 kubectl aply #新建 更新 升级: 滚动升级&#x…

Find a way (DFS BFS)

//新生训练 #include <iostream> #include <algorithm> #include <queue> using namespace std; using PII pair<int, int>;const int N 205; const int inf 0x3f3f3f3f; int n, m; int l;int dis1[N][N]; int dis2[N][N];int dx[] {0, 0, -1, 1};…

HarmonyOS入门--页面和自定义组件生命周期

文章目录 页面和自定义组件生命周期页面生命周期组件生命周期生命周期的调用时机 页面和自定义组件生命周期 生命周期流程如下图所示&#xff0c;下图展示的是被Entry装饰的组件&#xff08;首页&#xff09;生命周期。 自定义组件和页面的关系&#xff1a; 自定义组件&…

zabbix绑定钉钉进行通知,网页端添加JavaScript,无脑式操作

文章目录 前言一、编辑zabbix告警JavaScript脚本二、代码如下:编辑消息模板,自定义markdown格式的消息。总结前言 随着人工智能的不断发展,zabbix监控这门技术也越来越重要,一下进入正题。 一、编辑zabbix告警JavaScript脚本 没有没接可以新增媒介 其中URL是你的机器人地…

Linux系统——网络管理

此文章以红帽Linux9版本为例进行讲解。 红帽Linux9版本的网络管理十分全面&#xff0c;可在多处进行网络配置的修改&#xff0c;但需要注意的是&#xff0c;在9版本内&#xff0c;用户可在配置文件内进行网络配置的修改&#xff0c;但系统不会执行修改的命令&#xff0c;而在9之…

redis之主从复制、哨兵模式

一 redis群集有三种模式 主从复制&#xff1a; 主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可用的。 主从复制主要实现了数据的多机备份&#xff0c;以及对于读操作的负载均衡和简单的故障恢复。 缺陷&#xff1a; 故障恢复无法自动化&…

西圣、飞利浦、万魔开放式耳机好用吗?热门产品硬核测评对比

在无线耳机市场中&#xff0c;开放式耳机因其独特的佩戴方式和出色的听音体验逐渐受到消费者的青睐&#xff0c;西圣、飞利浦、万魔作为业内知名品牌&#xff0c;都推出了各自的开放式耳机产品&#xff0c;备受关注&#xff0c;那么这些热门品牌的开放式耳机究竟好用吗&#xf…

力扣热门算法题 174. 地下城游戏,189. 轮转数组,198. 打家劫舍

174. 地下城游戏&#xff0c;189. 轮转数组&#xff0c;198. 打家劫舍&#xff0c;每题做详细思路梳理&#xff0c;配套Python&Java双语代码&#xff0c; 2024.03.31 可通过leetcode所有测试用例。 目录 174. 地下城游戏 解题思路 完整代码 Python Java 189. 轮转数…

【Web应用技术基础】JavaScript(1)——案例:猜数字

上一个博客发了视频。这个博客因为不能插入视频&#xff0c;所以给大家一张一张截图的 点击“重新开始一局游戏” <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"…

[leetcode]剑指 Offer 29. 顺时针打印矩阵

前言&#xff1a;剑指offer刷题系列 问题&#xff1a; 输入一个矩阵&#xff0c;按照从外向里以顺时针的顺序依次打印出每一个数字。 示例&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5]思路&#xff1a; 看到这个…

Lambda表达式,Stream流

文章目录 Lambda表达式作用前提函数式接口特点 语法省略模式和匿名对象类的区别 Stream流思想作用三类方法获取方法单列集合(Collection[List,Set双列集合Map(不能直接获取)数组同一类型元素(Stream中的静态方法) 常见的中间方法终结方法收集方法 Optional类 Lambda表达式 作用…

客户案例丨拓数派向量计算引擎PieCloudVector助力东吴证券AIGC应用升级

1.项目背景 随着人工智能技术的不断创新和应用&#xff0c;我们可以看到人工智能在各个领域的应用越来越广泛。深度学习技术在图像识别、语音识别、自然语言处理等领域表现出色。机器学习算法的改进将解决更多实际问题&#xff0c;如增强学习、迁移学习和联合学习等&#xff…