首页 > 其他分享 >接口幂等的几种方案

接口幂等的几种方案

时间:2024-07-16 23:29:49浏览次数:13  
标签:方案 事务 数据库 接口 几种 documentBO 操作 数据 id

接口幂等

防重:防重设计主要为了避免产生重复数据

接口幂等性:除了防止产生重复数据,还要求执行多次与执行一次产生的影响是一样的

重复提交是造成的原因
幂等性是要保证的结果

针对操作

查询、删除具有幂等性

新增、修改不具有幂等性
update操作
1.如果只是单纯的更新数据,比如:update user set status=1 where id=1,是没有问题的。
2.如果还有计算比如累加,比如:update user set status=status+1 where id=1,这种情况下多次请求,可能会导致数据错误。

实际案例

单据审核后,然后记录相关流水记录

public void updateNoticeLock(Integer documentId) {
    // 1.查询
    DocumentBO documentBO = this.sysUserMapper.selectDocument(documentId);
    // 2.幂等校验
    if (documentBO.getStatus().equals("1")) {
        throw new RuntimeException("单据已审核");
    }
    // 3.记录流水
    this.sysUserMapper.approve(documentBO);

    documentBO.setCreateTime(LocalDateTime.now());
    // 4.插入流水记录
    this.sysUserMapper.insertDocumentFlows(documentBO);
}

解决方案一:数据库互斥锁

  • 依赖数据库本身的锁机制实现并发事务数据修改数据串行操作

select ... for update;

// 先锁住记录,不让别的事务操作,也是保证了查询与更新是原子性操作
select id,ware_name,num from table where id =1 for update;

update table set num = num -1 where id = 1;
  • 注意事项:

    1. 这种加锁的方式要注意对where查询的条件加索引,防止锁表
    2. for update要与事务配合使用才能生效,理由如下
    3. 相对于乐观并发机制,这中适合场景是并发量较多的情况下,但是这种加锁的方式可能会造成系统线程阻塞过多,会影响接口性能。

配合事务一块使用的原因:

这种是依赖数据库本身的锁机制,在执行查询时候就把数据锁住,让其他操作这条记录的事务等待。要知道数据库的锁针对的对象是一个个事务,换句话说数据库提供的锁能保证多个事务对同一条数据时操作时是串行执行的。

当一个事务A中某一块逻辑对某条记录进行修改时,会对当前记录进行加锁,若这时有其他事务B处理该记录时,就会等待前面事务A执行完,当事务内全部所有操作`提交后`,唤醒事务B再对该数据进行修改,所以这种依赖数据库的锁是能够保证事务并行执行的

解决方案二:乐观锁

口诀:查询、判断、失败延迟重试

在真正更新操作时候判断数据是否有冲突,即当前数据是否被其他操作已经篡改过,如果已被篡改,将重新检测是否冲突。

实现方法一般使用单项递增的版本号、时间戳,比如使用单项递增的版本号,当前操作先获取版本号,判断当前版本号与数据库中数据的版本号是否一致,如果一致说明此时没有其他操作正在执行,即可更新;如果不一致那么这次操作即失败,可以延迟重新调用当前方法,再次获取当前数据的版本号,再次进行冲突检测。

缺点:

  1. 适用于并发量低的场景下,当并发量高说明冲突越严重,那么循环调用的次数就多,吞吐量就低。
  2. 注意递归调用时要加睡眠时间,防止出现栈内存溢出
  3. 随着并发量越高,吞吐量会越来越低

案例1(冲突检测失败则不再重试,存在失败操作)

业务逻辑:单据审核,审核成功即添加流水

加事务@Tx

// 查询
DocumentBO documentBO = this.sysUserMapper.selectDocument(documentId);
// 幂等校验
if (documentBO.getStatus().equals("1")) {
    throw new RuntimeException("单据已审核");
}
// UPDATE t_document SET status =1 WHERE id = #{id} AND status = 0
int approveCount = this.sysUserMapper.approveStateMachine(documentBO);
if (approveCount == 1) {
    documentBO.setCreateTime(LocalDateTime.now());
    this.sysUserMapper.insertDocumentFlows(documentBO);
}

1. 使用单向递增的版本号或单向递增的时间戳。
2. 使用状态机方式。如果有状态机流转的方式,就可以使用此种方式。比如订单状态,1表示已创建、2表示已提交、3表示已支付。一方面是因为状态是定向流转的,另一方面也是利用数据库本身的锁机制,多个事务并发操作同一条数据,只有一个事务能执行成功

注意:此种方式要保证以上操作在同一个事务内,因为依赖的是数据库层面的锁。

案例2(保证所有操作执行成功)

业务逻辑:扣库存

public void checkAndLock() {

    // 先查询库存是否充足(5000)
    Stock stock = this.stockMapper.selectById(1L);

    // 再减库存
    if (stock.getCount() <= 0){
        return;
    }

    stock.setCount(stock.getCount() - 1);
    // 匹配行数为0,则再次调用当前方法,进行冲突检查重试
    if (this.stockMapper.updateById2(stock) == 0) {
        this.checkAndLock();
    }
}

<update id="updateById2">
    UPDATE `db_stock`
    SET `count` = #{count},version = version + 1
	WHERE `id` = #{id} AND version = #{version}
</update>

注意:

1. 并发量越低,吞吐量越高
适用于并发低的场景下,并发高时会导致冲突严重,循环调用消耗cpu,吞吐量低。
2. 不可加事务,由于当前事务尚未提交,很难读取到其他事务更新后的数据(RR根本读不到,RC能读到)

解决方案三:分布式锁

口诀:一锁、二幂等校验、三更新(新增)

  • 基于redis分布式锁

  • 基于zookeeper实现的分布式锁

    能够保证查询与修改是原子性操作,不会出现数据竞争的问题

操作步骤

一锁、二判断幂等、三操作(更新、插入)

以redis实现的分布式锁为例

解决方案四:建防重表

比如可能出现消息重复消费的情况时。新增消费消费表,当业务处理完后同时记录消息到消息消费表中,保证业务处理与记录消费消息是一致性的。下次有重复消费,判断该消息是否已经存在,如果存在表示已经消费,否则正常执行业务

总结与思考

要保证幂等性,本质上其实要保证幂等校验与操作数据是原子逻辑,如果能依赖数据库本身锁机制实现事务串行执行是最好的了,比如乐观锁种使用递增的版本号,或者状态机判断。像java层面使用悲观锁性能消耗是线程的阻塞与唤醒,而乐观锁实现依赖于数据库层面的锁,多个事务对同一个数据进行修改,会让事务同步执行该数据,也就是说一个事务对数据修改必须要等待另外一个对相同数据修改的事务提交后才可以执行。

一般逻辑在实现更新插入数据时,要进行幂等判断(查询、判断条件是否符合,如果符合则直接中断程序)
遵循一锁、二判、三更新、四释放原则
能依赖数据库本身的锁机制,能存在状态机流转,一定要加上状态条件。

同时重试通常跟幂等组合使用

参考
https://blog.csdn.net/crazymakercircle/article/details/135659360

标签:方案,事务,数据库,接口,几种,documentBO,操作,数据,id
From: https://www.cnblogs.com/party-abu/p/18306328

相关文章

  • 学之思系统测试报告(接口测试postman+性能测试jmeter+自动化测试selenium)
    学之思系统测试报告(接口测试postman+性能测试jmeter+自动化测试selenium)自动化测试脚本和执行学之思管理端+学之思学生端每个测试均需提交测试用例文档接口测试用例接口测试需要有缺陷文档性能测试每个接口需要提供聚合报告(每次请求前,清理数据)(测试计划+功......
  • 生成自己的网易云音乐接口--音乐插件功能
    获取自己的网易云接口用途:该接口用于个人博客添加音乐功能,详情可以参加我的个人博客:https://mebius.fun/公益api请求地址:GEThttps://api.yimian.xyz/msc/在网易云网页版歌单页面查看地址栏:例如我的歌单页面地址:网易云音乐(163.com)api地址:api.yimian.xyz/msc/?typ......
  • js需要同时发起百条接口请求怎么办?--通过Promise实现分批处理接口请求
    如何通过Promise实现百条接口请求?实际项目中遇到需要发起上百条Promise接口请求怎么办?前言不知你项目中有没有遇到过这样的情况,反正我的实际工作项目中真的遇到了这种玩意,一个接口获取一份列表,列表中的每一项都有一个属性需要通过另一个请求来逐一赋值,然后就有了这份封装真......
  • 【题解】金明的预算方案
    原题传送门题目描述金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过\(n\)元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附......
  • TS 入门(五):TypeScript接口与类
    目录前言回顾高级类型与类型操作1.接口a.基本接口b.可选属性和只读属性c.函数类型接口d.可索引类型e.接口继承2.类a.基本类b.类的成员(属性和方法)c.构造函数d.继承和派生类e.公有、私有和受保护的修饰符f.静态属性和方法g.抽象类扩展知识点:接口与类的结合......
  • 常见的定时任务方案
    常见的延时任务方案1、最轻量级(基于内存的线程池实现)一般用于短时间实时性较高,容许少量消息丢失importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Component;importjava.util.concurrent.*;@Slf4j@ComponentpublicclassMessageSender{......
  • 智能守护校园餐桌:校园阳光食堂视频AI监控智能管理方案
    一、背景分析随着科技的飞速发展,智能化、信息化已成为现代校园管理的重要趋势。校园食堂作为学校重要的服务设施,其食品安全、环境卫生和秩序管理显得尤为重要。作为校园生活中不可或缺的一部分,食堂的管理也急需引入先进技术,以提高食品安全、提升服务质量、优化就餐环境。为此,我们......
  • AvalonMM接口Interval Timer IP的寄存器介绍和Interval Timer寄存器读写操作详解
    一、间隔定时器结构间隔定时器的结构框图: 该间隔定时器有如下两个特点:-Avalon-MM接口,提供对6个16位寄存器的访问;-有一个脉冲输出接口(可选),可用作周期性脉冲发生器;该间隔定时器的所有寄存器都是16位的,可兼容16-bit和32-bit处理器。某些寄存器只存在于特定的配置中,例如,当该......
  • 园区道路车辆智能管控视频解决方案,打造安全畅通的园区交通环境
    一、背景需求分析随着企业园区的快速发展和扩张,道路车辆管理成为了保障园区秩序、提升运营效率及确保员工安全的重要任务。针对这一需求,旭帆科技TSINGSEE青犀提出了一种企业园区道路车辆管控的解决方案,通过整合视频监控、智能识别等技术,实现园区内车辆的有序管理,提升安全管理水平......
  • 智慧煤矿:AI视频智能监管解决方案引领行业新变革
    随着科技的飞速发展,人工智能(AI)技术已经渗透到各个行业,为传统产业的转型升级提供了强大的动力。在煤矿行业中,安全监管一直是一个重要的议题。为了提高煤矿的安全生产水平,降低事故发生率,智慧煤矿的概念应运而生,它结合了先进的AI智能监管技术,为煤矿的安全生产、高效运营和可持续发展......