Transaction - 记一次 Spring 事务联合 Redis 挂了引发的生产事故

2023-09-20 18:04:59

问题描述

java.lang.RuntimeException: java.lang.IllegalStateException: Already value […数据源信息…] bound to thread [[Ljava.lang.String;@231b1ae0.container-0-C-1]

上述问题是本次问题的最终结果,但并不是问题主因,之所以会引发这个问题,还需要先梳理下,我们的业务流程:针对一次入库的数据进行更新维护,先删除后新增,并且开了多线程,那么问题来了……

原因分析

我们的针对多线程事务 + 多数据源设计思路

  1. 动态数据源方法
  2. 手动更新多线程集合事务方案
  3. 手动提交/回滚事务方案(多线程)
  4. 必须在 finally 里执行,保证一定会被执行到

因为本次 Redis 突然 OOM 被打挂了,导致业务流程突然报错中断,那么如果这时候已经进入了事务操作……因为牵扯到多线程事务处理,我们需要先执行第 2 步

private void updateTranSyncManager(List<TransactionStatus> transactionStatusList) {
        // err: No value for key [DynamicRoutingDataSource] bound to thread
        TransactionSynchronizationManager.bindResource(dynamicRoutingDataSource, dynamicRoutingDataSource);
        // err: Transaction synchronization is not active
        TransactionSynchronizationManager.initSynchronization();
}

把本次要提交的数据源绑定到事务管理器里,使得在第 3 步的提交或者回滚事务的时候,可以处理好本次的数据源信息,至于为什么要这么做,问 Spring,因为源码里就是要这么操作的,所以这边要遵守这个规则。

private boolean transactionHandle(List<TransactionStatus> transactionStatusList, AtomicBoolean isError) {
        // 事务统一提交/回滚检验
        if (!transactionStatusList.isEmpty()) {
            if (isError.get()) {
                // 有错误, 回滚
                log.info("事务回滚...");
                transactionManager.rollback(transactionStatusList.get(0));
                log.info("事务回滚成功...");
            } else {
                // 无错误, 提交
                log.info("事务提交...");
                transactionManager.commit(transactionStatusList.get(0));
                log.info("事务提交成功...");
                return true;
            }
        }
        return false;
}

Tips1:顺便提一嘴,为什么这个是使用 get(0) 而不是整个 List 扔进去呢,因为看源码发现,因为我们业务是在同一个数据源里操作,所以只需要提交第一个即可,虽然 List 因为业务因素会添加很多,猜想里面数据源是引用对象,所以会自动处理相等对象的逻辑

Tip2:非常关键,updateTranSyncManager & transactionHandle 这 2 个方法必须保持一致性,怎么理解呢?就是 bindResource 后必须要执行 commit 或 rollback 方法,否则就会引起不一致性,导致上面的报错,要么就 2 个方法都不要执行,也是一种一致性

好了,精彩的故事正式开始……

有了上面的基础概念后,我们看这个 Redis 如果挂了的话,比如此时正好在业务处理的时候报错了呢?那么很有可能 DELETE 和 INSERT 操作没做完,导致事务 transactionStatusList 没有执行 add 操作,那么就会到 updateTranSyncManager 这方法里进行 bindResource,但是呢,等到进入 transactionHandle 方法里的时候,因为 List 是空的,所以不会执行 commit 或 rollback 操作,那么就会违背我们上面的规则(没有达成一致性),我们也称为事务管理器里的数据源混乱现象。

Ps:transactionStatusList 这个的入口是在执行 INSERT 或 UPDATE 或 INSERT 操作的时候,塞进去的每一次执行是一个事务

解决方案

很明显,因为上面 updateTranSyncManager  没有对 List 进行判空处理,导致不一致现象发生,修改完代码如下

private void updateTranSyncManager(List<TransactionStatus> transactionStatusList) {
    if (!transactionStatusList.isEmpty()) {
        // err: No value for key [DynamicRoutingDataSource] bound to thread
        TransactionSynchronizationManager.bindResource(dynamicRoutingDataSource, dynamicRoutingDataSource);
        // err: Transaction synchronization is not active
        TransactionSynchronizationManager.initSynchronization();
    }
}
更多推荐

知识图谱(4)图算法

基于图有很多任务,比如:节点分类:预测哪些网站是诈骗网站;关系预测:判断图中两个节点的关系;图分类:分子性质预测;聚类:社交网络分析,将相似用户聚类在一起,再推荐适合该簇的商品;图生成:药物分子生成,药物发现;目录图基础内容图遍历图聚类Node2Vec图基础内容节点的度:节点的边的数量。对于有向图,度还可以分为入度和出

植物大战僵尸各种僵尸攻略(四)

前言此文章为“植物大战僵尸”专栏中的011刊(2023年9月第十刊),欢迎订阅。版权所有。注意:1.本博客适用于pvz无名版;2.pvz指植物大战僵尸(PlantsVSZonbies);3.本文以耗费低做标准,方法不唯一;4.本期讲述难度较中型的僵尸。各种僵尸攻略潜水僵尸潜水僵尸有两种形态:第一种是普通形态;第二种是莲

虚拟人三维动画宣传片案例分享 | 广州“五羊”城市文化IP商业体裸眼3D广告影片

随着时代的发展+元宇宙的助推,裸眼3D形式的宣传方式逐渐出现在大众眼前。以数字人IP的3D立体效果吸引大众目光,让其驻足拍照、录视频分享至社交平台,为企业品牌带来高频传播价值。近日,广州“五羊”城市文化IP裸眼3D广告宣传片在广州黄埔大悦汇商业体精彩亮相,极具冲击力的视觉效果,令过往市民旅客大饱眼福,赋予城市现代感,打

LabVIEW使用PID对激振器控制

LabVIEW使用PID对激振器控制LabVIEW的PID在许多项目中都会用到,比如温度控制、压力控制、流量控制等,一般用自带的PID通过调节参数的值,基本上都可以解决。现在项目的情况是,通过正弦波的输出控制激振器,输出正弦波的幅值与激振器的运动距离相关。波形输出是通过阿尔泰PCI5655实现,激振器也是国产。通过采集

提交本地项目到GitHub

文章目录1下载git1.1通过homebrew安装Git1.2通过Xcode安装2创建sshkey、配置git3提交本地项目到GitHub说明:该博文参考这篇文章和这段视频1下载git1.1通过homebrew安装Git1、未安装homebrew,需安装homebrew/usr/bin/ruby-e"$(curl-fs

Unity中Shader的屏幕抓取 GrabPass

文章目录前言一、抓取1、抓取指令2、在使用抓取的屏幕前,需要像使用属性一样定义一下,_GrabTexture这个名字是Unity定义好的前言Unity中Shader的屏幕抓取GrabPass一、抓取1、抓取指令屏幕的抓取需要使用一个PassGrabPass{}GrabPass{“NAME”}2、在使用抓取的屏幕前,需要

Apache Doris 快速入门

1.基本概念FE,Frontend,前端节点,接收用户查询请求,SQL解析,执行计划生成,元数据管理,节点管理等BE,Backend,后端节点,数据存储,执行查询计划。前端节点FE和后端节点BE各自独立运行,互不影响。broker:用来和外部文件系统打交道2.修改配置DORIS_HOME=/export/server/

【数据结构】&&【C++】红黑树RBTree的模拟实现(平衡搜索二叉树)

【数据结构】&&【C++】红黑树的模拟实现(平衡搜索二叉树)一.红黑树的性质二.红黑树的模拟实现1.结点的定义2.搜索树的插入3.变色+向上处理4.旋转+变色三.红黑树与AVL树的差别四.完整代码一.红黑树的性质1.什么是红黑树?红黑树是一种搜索二叉树,但又在搜索树的基础上,在每个结点上增加一个存储位表示结点的颜色,颜

国际版阿里云/腾讯云免开户:云存储服务:云存储服务能够让你随时随地拜访和同享文件

云存储服务:云存储服务能够让你随时随地拜访和同享文件云存储服务是一种基于云技术的存储渠道,能够让用户存储、管理和同享各种类型的数据文件,如文档、图片、视频、音频等。这种服务具有许多长处,以下是对其进行的详细分析:长处:​随时随地拜访:云存储服务答应用户从任何地方、任何设备拜访文件,只要有互联网衔接即可。这对于在家办公、

Apache解析漏洞复现

一、多后缀解析漏洞1.漏洞说明配置apache时,对于apache配置不熟练,配置命令不清楚,在配置PHP文件处理程序时,配置命令存在问题:位于漏洞环境目录的conf/docker-php.conf里的配置命令(AddHandlerapplication/x-httpd-php.php),该命令会将后缀中只要含有.ph

金融风控建模常用指标介绍(WOE, IV, KS, PSI)

金融风控建模常用指标介绍(WOE,IV,KS,PSI)近期在做金融风控相关项目,有必要把特征和模型的衡量指标总结下,以备不时之需。这次主要介绍4个指标(WOE,IV,KS,PSI)。WOE(WeightofEvidence,用于特征变换,衡量变量某个取值的预测能力)WOE算法已在我的另一篇文章数据预处理-分箱(Binn

热文推荐