当前位置: 首页 > news >正文

[转载]数据库锁分布式锁实现接口幂等性

原文:https://mp.weixin.qq.com/s/sR4zUZsv408JDczvAv6_gA

在我们的实际开发中,会由于接口超时后重试机制、MQ的重复消费等场景都会带来接口幂等性的问题,下面我们介绍几种通过锁的方式实现接口幂等性的方案。

1、数据库唯一主键

数据库唯一主键的实现原理是使用数据库中主键唯一约束的特性,一般来说唯一主键比较适用于添加数据时的幂等性场景,唯一主键能保证一张表中只能存在一条该唯一主键的记录,方案的实现的流程如下图所示:

在这里插入图片描述
使用数据库唯一主键实现接口幂等性时需要注意的是,为了保证在分布式环境下ID的全局唯一性,这里的主键一般使用分布式ID充当主键,常见的几种生成分布式ID的方案的整理如下:

整理10种分布式id生成方案

2、数据库悲观锁

假设我们查询到某个订单A后,然后更新订单的状态,此时就可以使用数据库的悲观锁实现。使用悲观锁可以添加for update关键字,即对这行记录上锁(上行锁还是表锁取决于where后面的查询字段索引的类型,假设订单的id是唯一主键,那么就是行锁),如下图所示:
在这里插入图片描述
使用的查询语句如下所示:

select * from order where order_id = 123 for update
若是线程T1执行了select … for update,此时就会对这行记录上锁了,那么直到整个线程T1提交事务之前,T2线程来查询相同的订单id数据会进入到阻塞状态。当T1完成业务处理之后释放锁,其他的线程才可以继续操作这条数据。
悲观锁在同一事务操作过程中锁住了一行数据,别的请求过来只能等待,如果当前事务耗时比较长,就很影响接口性能。对于Mysql数据库,要求存储引擎必须用innodb,因为innodb才支持事务,另外orderId字段一定要是主键或者唯一索引,不然会锁整张表。

3、数据库乐观锁

数据库乐观锁方案一般适用于执行"更新操作"的过程,我们可以提前在对应的数据表中额外添加一个字段来充当数据的版本标识。这样每次对该数据表的这条数据执行更新时,都会将该版本标识作为一个条件,值为上次待更新数据中的版本标识的值,如下图所示的:
在这里插入图片描述
每次更新的时候,我们使用版本号来进行字段校验以及进行update更新,sql如下所示:
UPDATE order SET status=2,version=version+1 WHERE id=123
AND version=0;
在WHERE后面的条件id=123 AND version=0 被执行后,id=123的 version被更新为1,所以如果重复执行该条sql语句将不生效,因为id=123 AND version=0的数据已经不存在,通过这样方式就能保住更新的幂等,多次更新对结果不会产生影响。

4、状态机机制

在业务表中有状态的时候,如订单表中的订单状态(待提交、待支付、待发货、待收货等等)这些状态的值是有规律的,按照业务节点一级级的流转,那么我们就能利用这个特性实现接口的幂等,如下如所示的节点状态流转过程:
在这里插入图片描述

假如订单id为123的订单当前状态是待支付,当用户支付后,订单的状态要变成待发货,更新的语句如下:

update order set status = 2 where order_id = 123 and status = 1
线程T1请求时,该订单的状态是待支付(status = 1),所以该update语句可以正常更新数据,sql执行结果的影响行数是1,订单状态变成了2。
线程T2相同的请求过来,再执行相同的sql时,由于订单状态变成了2,再用status=1作为条件,无法查询出需要更新的数据,所以最终sql执行结果的影响行数是0,即不会真正的更新数据。数据表记录中的状态来作为条件做更新其实跟乐观锁使用版本号类似。

5、分布式锁

唯一主键与乐观锁的本质是使用了数据库的锁,但由于数据库锁的性能不太好,所以我们可使用Redis、Zookeeper等中间件来实现分布式锁的功能,以Redis为例实现幂等的流程图:
在这里插入图片描述
当用户通过浏览器发起请求,服务端接收到请求后,使用唯一标识(如订单号)作为Redis的key。然后使用Redis的set命令,将该唯一标识存储到Redis中并设置超时时间,业务根据Redis返回的结果做不同的处理:
(1)如果保存Redis成功,那么允许当前的线程执行业务操作。
(2)若保存Redis失败,说明是重复请求,则直接响应客户端,请求处理成功。
在分布式锁中要设置一个合理的过期时间,如果设置过短,无法有效的防止重复请求;如果设置过长,占用了Redis的存储空间,在请求非常多的情况下会给Redis造成压力。

总结

每种幂等性实现方案都有各自的优缺点,我们需要根据实际的业务场景选择更合适的方案来解决幂等性问题。

http://www.xdnf.cn/news/1078075.html

相关文章:

  • 如何将文件从 iPhone 传输到 Android(新指南)
  • BUUCTF在线评测-练习场-WebCTF习题[ZJCTF 2019]NiZhuanSiWei1-flag获取、解析
  • 02-更换证件背景
  • 节点小宝内网穿透实测:告别“无网”烦恼,让你的设备“触手可及”
  • python实现基于资金分布、RSI及布林策略的方案
  • 智慧赋能高压并网:分布式光伏监控系统在5.88MW物流园项目的实践解析
  • [环境配置] 3. 使用 UV管理 Python 环境
  • 416. 分割等和子集
  • docker拉取redis并使用
  • STEP-BACK PROMPTING:退一步:通过抽象在大型语言模型中唤起推理能力
  • MySQL的5.0和8.0版本区别
  • 基于[coze][dify]搭建一个智能体工作流,使用第三方插件抓取热门视频数据,自动存入在线表格
  • vscode 下 LaTeX 使用配置
  • (一)大语言模型的关键技术<-AI大模型构建
  • Redis搭建集群模式
  • 微信小程序入门实例_____打造你的专属单词速记小程序
  • MAC 多应用切换技巧,单应用切换技巧
  • 文心快码答用户问|Comate AI IDE专场
  • C#调用C++导出的dll怎么调试进入C++ DLL源码
  • 生产环境下载功能OOM问题复盘
  • 学习笔记(29):训练集与测试集划分详解:train_test_split 函数深度解析
  • 科技有温度:七彩喜智慧康养平台,为银发生活织就“数字守护网”
  • 【Vue入门学习笔记】Vue核心语法
  • 飞算 JavaAI 智控引擎:全链路开发自动化新图景
  • Active-Prompt:让AI更智能地学习推理的革命性技术
  • 纹理贴图算法研究论文综述
  • 【leetcode算法300】:哈希板块
  • Stereolabs ZED系列与ZED X立体相机系列对比:如何根据项目需求选择?
  • Kalibr解毒填坑(一):相机标定失败
  • .net审计库:EntityFrameworkCore.Audit