后端|一个分布式锁「失效」的案例分析

news2025/1/10 21:20:56

小猿最近很苦恼:明明加了分布式锁,为什么并发还是会出问题呢?

故事从接到需求开始说起。

接到需求

小猿前一阵接到一个小任务,里面有一个功能对应的场景如下:

  • 封装一个对账户余额进行加减操作的方法;

  • 所属服务部署了多个实例;

  • 这个方法可能会有并发调用。

注:实际业务场景比较复杂,已做简化。

小猿略作思考,就抓住了关键点:余额操作——要注意事务,多实例——要注意并发。

小猿的原始代码如下:

@Override
@Lock(key = "#accountNo")
@Transactional(rollbackFor = Exception.class)
public void updateBalance(String accountNo, AmountOperateParam param) {
    // do something
}

可以看到,这个方法上通过注解方式加了分布式锁和事务,锁的 key 是 accountNo,也就是账户业务主键。

自测和测试也没发现啥问题,就高高兴兴发完版回家了。

出问题了

第二天一早,就接到少量用户反馈,说自己的账户余额不对了。

小猿的第一反应是:我这块自测和测试都没问题,其它功能导致的吧?本地又是一通自测,也没有复现问题。但谨慎起见,还是往代码里加了一些日志,来确认是不是自己的方法引发的。

当又有用户反馈时,小猿根据日志的情况确认了:还真是自己方法的问题,对同一个账户的余额操作,多个并发请求会同时执行到方法体里面。

也就是说……分布式锁没锁住?

冥思苦想了好久,又在本地做了大量的测试,终于让小猿找到了问题所在:AOP 执行顺序问题。

小猿设计的时序:

73824f5d25322e9fb664673fe3c3ef2f.png

但实际的时序:

eb4b854077e7eafc932c8138f845562a.png

也就是说期望是这样的执行顺序:

a33a370cc0a207f9e9e00752a75f54bd.png

但实际的执行顺序:

cfb0d65a724fdcfa4ebff2b000d0948d.png

分布式锁和事务,都是通过 AOP 来实现的,而 AOP 的执行顺序是根据切面的优先级来的,而小猿的分布式锁切面的优先级比事务切面的优先级低,所以就出现了上面的时序问题。

于是通过给分布式锁的切面指定 Order 的方式,让它的优先级高于事务切面(注:Order 值越小,执行优先级越高),验证完没问题后,就又高高兴兴地更新完版本,修复好历史问题数据后回家了。

还有问题

谁知道第二天一早,还是有极少量的用户反馈账户余额不对的问题。

这次小猿就有点懵了,为什么还会出现这种情况呢?

经过一番艰苦卓绝的排查,终于找到了问题所在:事务嵌套。

从前文中的示例代码中可以看到,小猿的方法上加了事务注解 @Transactional(rollbackFor = Exception.class) 里,没有指定事务的传播行为,默认是 Propagation.REQUIRED,也就是说如果当前没有事务,就新建一个事务;如果当前有事务,就加入到当前事务中。

小猿自己写的代码里没有在事务方法里嵌套调用这个方法的情况,但是同事写的代码里有,这样就会导致前文的时序问题再次发生。

找到问题就好办了,小猿将自己的方法上的事务传播行为改成了 Propagation.REQUIRES_NEW,也就是说如果当前没有事务,就新建一个事务;如果当前有事务,就将当前事务挂起,新建一个事务。

这次更新完版本后,小猿就再也没有收到用户反馈了,终于可以安心回家睡觉了。

小结

在日常的开发过程中,如果涉及到并发和事务,一定要多留几个心眼,考虑周全,确认以下要点是否都正确实现:

  • 是否做了必要的并发控制?

  • 事务的传播行为是否符合预期?

  • AOP 的执行顺序是否符合预期?

  • 对并发的场景是否做了充分的测试?

  • 对于比较关键的操作,是否打印了必要的日志?


假如读完文章有收获,可以关注我的微信公众号「闷骚的程序员」并🌟设为星标🌟,随时内容。

帮忙点个「分享」或者「在看」吧!

⬇️⬇️⬇️

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

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

相关文章

shell指令,通过函数实现数组求和,通过函数获取用户uid和gid

一、实现一个对数组求和的函数&#xff0c;数组通过实参传递给函数 num0 read -p "请输入一组数据&#xff1a;" -a arr function add() {for ((i0; i<${#arr[*]}; i))do ((numarr[i]))donereturn $num } add ${arr[*]} echo $? 二、写一个函数&#xff0c;输出…

基于STM32智能环境系统

摘要 本系统采用stm32f407作为主控芯片&#xff0c;实现对环境的监测。并且通过和手机通信&#xff0c;获取当前的天气预报信息&#xff0c;结合当前测得的温湿度&#xff0c;可以为用户提供出行建议。利用stm32自带的RTC可以实现时间及闹钟功能。此外RTC还可以用于电子日历的…

《信息系统项目管理师教程(第4版)》第19章 配置与变更管理 知识点整理 xmind思维导图

已上传xmind思维导图&#xff0c;需要可下载 一、配置管理 基于配置库的变更控制(经常考) 二、变更管理

华为OD七日集训第4期 - 按算法分类,由易到难,循序渐进,玩转OD

目录 一、适合人群二、本期训练时间三、如何参加四、7日集训第4期五、精心挑选21道高频100分经典题目&#xff0c;作为入门。第1天、数据结构第2天、滑动窗口第3天、贪心算法第4天、二分查找第5天、分治递归第6天、深度优先搜索dfs算法第7天、宽度优选算法&#xff0c;回溯法 六…

Mybatis-Plus 使用教程

01-Mybatis-Plus介绍 1.1 什么是mybatis-plus 官网: 简介 | MyBatis-Plus MyBatis-Plus&#xff08;简称 MP&#xff09;是一个 MyBatis 的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 1.2 官方愿景 1.3 特性 无侵入&…

伪谱法地震波场数值模拟

本文实现内容 各向同性介质波动方程伪谱法波场求解。各项异性介质(VTI、HTI)介质伪谱法波场求解。实现了衰减边界条件、拓展周期边界法。一种波场模拟的数据存储格式.sfd&#xff0c;提供二进制或文本输入输出。对波场模拟得到的存储数据进行.gif绘制、.png绘制、地震剖面绘制…

MyBatis核心配置文件解析: 一步步深入理解mybatis-config.xml

&#x1f600;前言 在进行MyBatis项目开发时&#xff0c;合理和高效的配置是确保项目顺利进行的基础。其中&#xff0c;mybatis-config.xml配置文件扮演着极其重要的角色&#xff0c;它包含了MyBatis运行时的各种必要配置信息&#xff0c;如数据库连接属性、事务管理器配置、别…

vector容器的详解与分析

简介&#xff1a; vector容器在高级语言中运用非常广泛&#xff0c;此容器可看成C语言中的动态数组结构用来存储一系列数据&#xff0c;它不仅支持C语言数组中的所有使用方式&#xff0c;还支持vector在C中还有更高级的使用。在C往后的高级运用时&#xff0c;通常把一些常用的操…

基于Java web的医院分诊管理系统文档

摘要 医院分诊管理系统是适应时代发展的需要&#xff0c;提高管理的效率而开发设计的&#xff0c;有效的减少了患者排队取号的时间&#xff0c;增加了医生的工作效率。通过对信息的收集、存储、传递、统计、分析、综合查询、报表输出和信息共享&#xff0c;及时为医院领导及各部…

报错处理:Redis无法连接

报错环境&#xff1a; Linux Redis 具体报错&#xff1a; redis.exceptions.ConnectionError: Error 111 connecting to 127.0.0.1:6379. Connection refused. 排错思路&#xff1a; 当尝试连接Redis服务时&#xff0c;如果出现连接拒绝的错误&#xff0c;可能是由于Redis服务…

修正能力是智能的关键之一

智能包括事前预测、事中干预和事后反馈。这些方面相互关联&#xff0c;共同构成了一个完整的智能系统。 事前预测&#xff1a;智能系统可以通过分析数据、模式识别和机器学习等方法&#xff0c;进行事前预测。它可以根据已有的信息和历史数据&#xff0c;推测未来可能发生的情况…

csdn如何删除已发布的博客内容

首先&#xff0c;将鼠标移动到自己的头像&#xff0c;会显示内容管理 点击内容管理进入下方界面&#xff0c;选择文章&#xff0c;在想要删除的文章的后边的浏览旁边有三个点&#xff0c;点击后选择删除&#xff0c;删除后回到主页面刷新页面&#xff0c;会发现已发布的文章已经…

饲料添加剂 微生物 植物乳杆菌 学习记录

声明 本文是学习GB 7300.502-2023 饲料添加剂 第5部分&#xff1a;微生物 植物乳杆菌. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件规定了饲料添加剂植物乳杆菌的技术要求、采样、检验规则、标签、包装、运输、贮存和保质 期&#…

K8S 二进制部署

一、准备规划二、操作系统初始化配置2.1 关闭防火墙2.2 关闭selinux2.3 关闭swap2.4 根据规划设置主机名2.5 在master添加hosts2.6 调整内核参数 三、部署 docker引擎四、部署 etcd 集群4.1 准备签发证书环境4.2 生成Etcd证书4.3 创建用于存放 etcd 配置文件&#xff0c;命令文…

Java“牵手”淘宝商品列表页数据采集+商品价格数据排序,商品销量排序数据采集方法

采集场景 在淘宝首页&#xff08;https://s.taobao.com/&#xff09;输入关键词搜索&#xff0c;采集搜索后得到的商品列表页数据。示例中关键词为【耐克】&#xff0c;可根据需求进行更换&#xff0c;同时支持自动批量输入多个关键词。 采集字段 采集字段包括关键字文本值…

js如何实现一个简单的节流函数?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 实现简单的节流函数⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入…

模电课设:用Multisim简析三极管与场效应管放大电路

1 课设内容 1&#xff09;利用Multisim搭建基于晶体三极管的放大电路&#xff1b; 2&#xff09;利用Multisim搭建基于场效应管的放大电路&#xff1b; 2 模型搭建 我们首先要认清放大电路的概念。它指的是把输入微弱的电信号的功率放大&#xff0c;因为在多数情况下&#xf…

改善客户体验应该从哪几个方面入手?

在为客户提供良好使用体验的同时&#xff0c;还在针对性的为他们制定个性化服务&#xff0c;大多数公司都知道提供良好的客户体验的重要性&#xff0c;&#xff0c;那么如何为客户提供最佳的体验呢&#xff1f; 为客户提供最佳的体验需要从以下几方面入手&#xff1a; 了解客…

IP175G参考资料和引脚图

特性 5端口嵌入式10/100PHY开关控制器 支持5100BaseTX(IP175G)或4100BaseTX(85nm)技术&#xff0c;只需要3.3V单通道 1FX(IP175GH) 支持1KMAC地址表项 448K位包缓冲存储器 100MPHY支持IEEE8023az全双工 10MPHY只支持10BaseTe 支持自动MDI-MDIX功能 电源管理工具(PWMT)…

电工-PN结的工作原理

如果将PN结加正向电压&#xff0c;即P区接正极&#xff0c;N区接负极&#xff0c;如右图所示。由于外加电压的电场方向和PN结内电场方向相反。在外电场的作用下&#xff0c;内电场将会被削弱&#xff0c;使得阻挡层变窄&#xff0c;扩散运动因此增强。这样多数载流子将在外电场…