关于分布式锁的释放和spring事务提交时机不符合预期从而带来的数据不一致的问题

提要

首先注意,本文探讨的不是分布式事务,请读者注意区分!
在我们的日常开发种,分布式锁和spring事务是常见的两种控制数据一致性的方式。
分布式锁和spring事务各自的作用就不做阐述了,不是本文重点,本文重点阐述一下锁释放和事务提交的问题。

不一致的场景

下面这个场景是抽象出来的一个很简单的场景
我们业务种经常会遇到,比如线程A线程B同时调用test0
假如A先获取到锁,在test1中更新了x字段
在B获取到锁的时候,拿到的x的值是符合预期的么?

@SneakyThrows
@Override
public void test0() {CommonServiceImpl self = (CommonServiceImpl) AopContext.currentProxy();log.info("test0----------------------");self.test1();self.test2();
}@SneakyThrows
@Override
public void test1() {try {// 这里就是普通的一个加锁和释放锁的方法,在两个方法内分别打印个加锁和释放锁的日志LockUtil.acquireLock("test1");log.info("test1----------------------更新数据库里的某个字段x");Thread.sleep(5000);} finally {LockUtil.releaseLock("test1");}
}@SneakyThrows
@Override
public void test2() {log.info("test2----------------------做一些其他无关紧要的查询");Thread.sleep(5000);
}

显然是不符合预期的,B线程拿到的x大概率是A更新前的,如果这里是一些库存之类的场景,很可能就出现超卖的现象。
至于为什么,我们可以看下运行的打印结果
在这里插入图片描述
原因就在于锁的释放是在事务提交之前(当然这里的前提是这三个方法在一个事务中),B在拿到锁去查询x的值的时候,A虽然已经更新完了x的值并且释放了锁,但是事务还没来得及提交,所以B拿到的仍然是旧的值,此时去更新就会出问题。
那么什么情况下不会出问题呢?

  1. B线程获取到锁,去查询x的值的时候,A线程中test2的方法刚好执行完提交了事务,这个时候查询的值就是修改后正确的值(显然我们开发不能碰运气)
  2. 这几个方法都没有事务,B只要获取到锁,那么拿到的就是最新的数据(这种场景是存在的,而且我们开发中也提倡没必要用事务的场景就不需要用,需要的场景尽可能用小事务,当然这是开发之前就考虑的问题了)
  3. 某些数据库的事务隔离机制可能不存在这种场景(略有耳闻,但我确实没见过)
  4. 锁加在事务之外的时候,这样也可以保证锁的释放在事务提交之后(但是有时候避免不了事务传播和嵌套)
  5. 还有些场景,比如上面的代码中,哪怕test0只调用了test1(),也有可能出现上述场景,因为上面的锁本质上还是加在事务里面的,虽然它锁的内容包含了整个方法,但是遇到一些极端情况,比如数据库性能不太好的时候,就会出现线程B拿到锁的时候A的事务还没提交。
    所以,我们还是需要考虑,如果真存在这种场景,我们该怎么处理?

通过事务同步管理器手动控制

大家可以看下代码有什么区别

@SneakyThrows
@Override
public void test0() {CommonServiceImpl self = (CommonServiceImpl) AopContext.currentProxy();log.info("test0----------------------");self.test1();self.test2();
}@SneakyThrows
@Override
public void test1() {try {LockUtil.acquireLock("test1");log.info("test1----------------------更新数据库里的某个字段x");Thread.sleep(5000);} finally {LockUtil.unlockAfterTransaction("test1");}
}@SneakyThrows
@Override
public void test2() {log.info("test2----------------------做一些其他无关紧要的查询");Thread.sleep(5000);
}

先来上运行结果
在这里插入图片描述
很显然,这里的释放锁过程跑到了test2执行完成之后,也就是事务提交之后。
这样就保证了B拿到锁的时候,A的事务是已提交状态
这里主要就是把
LockUtil.releaseLock(“test1”)
换成了
LockUtil.unlockAfterTransaction(“test1”)

public static void unlockAfterTransaction(String lockName) {//事物完成后释放锁TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {@Overridepublic void afterCompletion(int status) {super.afterCompletion(status);releaseLock(lockName);}});}

就是把之前释放锁的方法包一层,通过事务同步管理器加个判断

事务同步管理器的其他用处

除了确保锁释放和事务提交的时机之外,事务同步管理器还可以有其他作用,比如你在一个方法中调用异步方法,当前事务提交成功之后调用和立刻调用就,结果可能完全不同,下面是一个简单的示例:

立刻调用

public void fireAsynchronous(final String type, final Object... parameters) {this.asyncEventListenerExecutor.execute(new Runnable() {public void run() {EventManager.this.fire(type, parameters);}});}

事务成功提交之后再调用

public void fireSyncOnTransactionSuccess(final String type, final Object... parameters) {TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {public void afterCommit() {EventManager.this.fire(type, parameters);}});}

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

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

相关文章

AttributeError: module ‘backend_interagg‘ has no attribute ‘FigureCanvas‘.

问题1: AttributeError: module backend_interagg has no attribute FigureCanvas. 解决方案: import matplotlib matplotlib.use(Agg) # 选择合适的后端,如Aggimport matplotlib.pyplot as plt在你的代码开头加上这两行代码,…

AIGC技术的发展现状与未来趋势

AIGC(人工智能生成内容)技术是近年来快速发展的领域之一,它涉及使用人工智能来创建或编辑内容,包括文本、图像、音乐和视频等。这项技术的进步为各个行业带来了革命性的变化,同时也引发了一系列伦理和风险问题。 一、技…

安卓一键换壁纸

360手机有一个很赞的应用&#xff0c;没有界面&#xff0c;点击一下图标换一张壁纸&#xff0c;其他手机都没有看到这个功能。 vivo倒是有亮屏换锁屏壁纸。 1.无界面应用 https://blog.51cto.com/u_16213305/8503774 AndroidManifest.xml 主题改为不显示 <activity an…

宜搜科技死磕港交所上市:从搜索引擎到广告投放,业绩疲态凸显

近日&#xff0c;宜搜科技控股有限公司&#xff08;下称“宜搜科技”&#xff09;向港交所递交招股书&#xff0c;计划在香港主板上市&#xff0c;中银国际为其独家保荐人。 值得注意的是&#xff0c;宜搜科技已在资本市场辗转多年。该公司曾于2014年向纽交所递交上市申请&…

CentOS7编译jsoncpp静态库

1. 官网下载源码 github地址&#xff1a;GitHub - open-source-parsers/jsoncpp at update 2. 编译 Unzip jsoncpp-master.zip Cd jsoncpp-master mkdir -p ./build/debug cd ./build/debug/ cmake -DCMAKE_BUILD_TYPEdebug -DBUILD_SHARED_LIBSOFF -DCMAKE_ARCHIVE_OUTPUT_D…

docker快速搭建部署mqtt

文章目录 前言一、mqtt是什么&#xff1f;二、使用步骤1.引入库2.创建临时容器3.创建挂在目录4.将临时容器的配置挂载到宿主机中5.删除临时容器6.运行容器并挂载文件7.登录EMQX内置的管理控制台 总结 前言 一、mqtt是什么&#xff1f; MQTT&#xff08;Message Queuing Teleme…

Ts类型体操详讲 之 extends infer (下)

目录 1、函数 &#xff08;1&#xff09;提取参数类型 &#xff08;2&#xff09;提取返回值类型 2、构造器 &#xff08;1&#xff09;提取构造器返回值 &#xff08;2&#xff09;提取构造器参数类型 3、索引类型 本章我们继续上节的内容继续&#xff0c;展示我们对ex…

送变电乙级资质从入门到成功申请

1. 初步了解与规划 研究资质标准&#xff1a;首先&#xff0c;详细研究最新的送变电乙级资质标准&#xff0c;包括企业资质条件、人员配置要求、技术设备标准等。企业准备&#xff1a;确保企业具备独立法人资格&#xff0c;注册资本达标&#xff0c;且企业信誉良好。制定计划&…

C++ //练习 13.24 如果本节中的HasPtr版本未定义析构函数,将会发生什么?如果未定义拷贝构造函数,将会发生什么?

C Primer&#xff08;第5版&#xff09; 练习 13.24 练习 13.24 如果本节中的HasPtr版本未定义析构函数&#xff0c;将会发生什么&#xff1f;如果未定义拷贝构造函数&#xff0c;将会发生什么&#xff1f; 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工…

华为OD机试真题-田忌赛马-2024年OD统一考试(C卷D卷)

题目描述: 给定两个只包含数字的数组a,b,调整数组 a 里面数字的顺序,使得尽可能多的 a[i] >b[i]。数组 a和 b 中的数字各不相同。 输出所有可以达到最优结果的 a 数组的数量 输入描述: 输入的第一行是数组 a 中的数字,其中只包含数字,每两个数字之间相隔一个空格,a…

提升你的C编程技能:使用cURL下载Kwai视频

概述 本文将介绍如何利用C语言以及cURL库来实现Kwai视频的下载。cURL作为一个功能强大的网络传输工具&#xff0c;能够在C语言环境下轻松地实现数据的传输。我们还将探讨如何运用代理IP技术&#xff0c;提升爬虫的匿名性和效率&#xff0c;以适应Kwai视频平台的发展趋势。 正…

报告!这里发现了一个赛博炼丹的神级平台!

众所周知&#xff0c;“赛博炼丹”是一个AI开发研究领域古老又神秘的活动&#xff0c;它往往对炼丹平台有很高的要求。如果你也是一路从“炼丹小白”成长到“资深AI算法工程师”&#xff0c;那你一定懂我在说什么&#xff1f;说好了&#xff0c;天台见&#xff01; GpuMall智算…

力扣HOT100 - 108. 将有序数组转换为二叉搜索树

解题思路&#xff1a; 二叉搜索树一般使用中序遍历 class Solution {public TreeNode sortedArrayToBST(int[] nums) {return helper(nums,0,nums.length-1);}public TreeNode helper(int[] nums,int left,int right){if(left>right) return null;//确定根节点//总是选择中…

Vue 3 + TypeScript + Vite 2024年4月最新管理系统基建

Vue 3 TypeScript Vite 2024年4月最新管理系统基建 相关依赖 vue: ^3.4.21vite: ^5.2.0typescript: ^5.2.2eslint: ^9.0.0 1. 初始化项目 1.1 node版本要求 node: v18.17.1 1.2. 创建项目 使用 PNPM: # 创建项目 pnpm create vite vue3-element-template --template …

【缓存服务】⭐️自定义实现一个简易的数据缓存

目录 &#x1f378;前言 &#x1f37b;手写缓存服务 &#xff08;1&#xff09;缓存实体类 &#xff08;2&#xff09;缓存工具类 &#xff08;3&#xff09;测试缓存服务 &#x1f377;已有的缓存工具 &#x1f379;章末 &#x1f378;前言 俗话说 有轮子不用 就是玩 开个…

WebStorm 中调试 (Debug) JavaScript 文件(js)

WebStorm 中调试 (Debug) JavaScript 文件(js) 在 WebStorm 中调试 JavaScript 文件&#xff0c;您可以设置断点&#xff0c;启动调试会话&#xff0c;并使用浏览器中的开发者工具来查看变量和执行流程。以下是详细步骤&#xff1a; 设置断点 打开您的 JavaScript 文件。 在…

使用 vllm 本地部署 Llama3-8b-Instruct

使用 vllm 本地部署 Llama3-8b-Instruct 0. 引言1. 安装 vllm2. 本地部署 Llama3-8b-Instruct 0. 引言 此文章主要介绍使用 vllm 运行 Llama3-8b。 1. 安装 vllm 创建虚拟环境&#xff0c; conda create -n myvllm python3.11 -y conda activate myvllm安装 Ray 和 Vllm&am…

条件生成对抗网络(cGAN)在AI去衣技术中的应用探索

随着深度学习技术的飞速发展&#xff0c;生成对抗网络&#xff08;GAN&#xff09;作为其中的一个重要分支&#xff0c;在图像生成、图像修复等领域展现出了强大的能力。其中&#xff0c;条件生成对抗网络&#xff08;cGAN&#xff09;通过引入条件变量来控制生成模型的输出&am…

Spring SpringBoot(详解)

1. Spring简介 1.1 Spring 核心设计思想 1.1.1 Spring 是什么&#xff1f; Spring 是包含了众多⼯具⽅法的 IoC 容器。Spring 指的是 Spring Framework&#xff08;Spring 框架&#xff09;&#xff0c;它是⼀个开源框架&#xff0c;Spring ⽀持⼴泛的应⽤场景&#xff0c;它…

SIT3088E:3.0V~5.5V 供电,14Mbps 半双工 RS485/RS422 收发器

特点&#xff1a; 3.0V~5.5V 宽电源范围&#xff0c;半双工&#xff1b;  总线端口 ESD 保护能力 HBM 达到 15kV 以上&#xff1b;  总线容错耐压达到15V&#xff1b;  1/8 单位负载&#xff0c;允许最多 256 个器件连接到总线&#xff1b;  驱动器短路输出保护&…