首页 > 其他分享 >不合理的执行顺序引发的死锁

不合理的执行顺序引发的死锁

时间:2024-04-22 18:11:54浏览次数:24  
标签:map 顺序 不合理 list update 死锁 id ct

问题现象

程序上线后运行反馈总会提示死锁,日志大量出现java.sql.BatchUpdateException: 批处理中出现错误: ORA-00060: 等待资源时检测到死锁

根据具体的异常堆栈信息找到对应的代码行,发现某个Service层有类似如下代码片段:【方法上有切面事务】

    public void batchUpdate(List<Map<String,Object>> list){
        String[] sqlArray = list.stream().map(map -> "update tb_test set ct = ct +1 where id='" + MapUtils.getString(map, "id") + "'").toArray(String[]::new);
        jdbcTemplate.batchUpdate(sqlArray);
    }

先不去纠结语法安全性问题,是否会有其他问题呢?
集合list是由外部传入的,然后这里根据外部传入形成sql,之后批量执行。现在偶发就会报上面的异常,提示死锁。因为外部的集合中的顺序是非固定的,也就是说并不是每次按照固定顺序传入的,那么形成的sql顺序也是随机的
比如:
线程T1传入的集合中id顺序为1,2,3;同一时刻
线程T2传入的集合中id顺序为3,1,2。此时就有可能会因为锁争用问题导致相互等待形成死锁,直到oracle自动检测并牺牲掉一个锁,然后另一个执行成功。

另外,根据版本信息查看到早前这里的代码是多了一行的,只不过由于业务调整用不到,注释了,也就是未注释之前是不会产生死锁的【因为串行执行了】

    public void batchUpdate(String id,List<Map<String,Object>> list){
	//早前是有这一行的。注意id是写死的...也就是无论多少个线程进来,只要当前事务没有完成(提交或者回滚),其他都得排队,其实就是串行了。。。当然不会死锁了。巧合编程。瞎写呗。
	jdbcTemplate.update("update tb_test2 set lk=1 where id=10018");
        String[] sqlArray = list.stream().map(map -> "update tb_test set ct = ct +1 where ct<100 and id='" + MapUtils.getString(map, "id") + "'").toArray(String[]::new);
        jdbcTemplate.batchUpdate(sqlArray);
    }

解决办法

  1. 非得要拼接SQL,那就在形成SQL之前,强行按照id排序,
public void batchUpdate(List<Map<String,Object>> list){
        String[] sqlArray = list.stream().sorted(Comparator.comparing(m -> MapUtils.getString(m, "id", "")))
                .map(map -> "update tb_test set ct = ct +1 where ct<100 and  id='" + MapUtils.getString(map, "id") + "'").toArray(String[]::new);
        jdbcTemplate.batchUpdate(sqlArray);
    }
  1. 使用NamedJdbcTemplate【推荐】
public void batchUpdate(List<Map<String,Object>> list){
      //TODO 校验集合,且保证传入id,
        String sql = "update tb_test set ct = ct +1 where ct<100 and  id=:id";
        namedjdbcTemplate.batchUpdate(sql,list);
    }

因为底层实现是不一样的(比如oracle的ojdbc驱动中OracleStatement、OraclePreparedStatement对于batchUpdate的实现是有区别的)。前一个是执行第一个语句的时候打开一个物理连接开始执行所有SQL,直到全部执行完成,也就是逐条执行,那这样如果每次传入的SQL顺序不一样,行锁获取和释放的顺序就不会一样。而OraclePreparedStatement是当成一个整体来执行,也就是一开始就加了锁。

注意事项

通常一个update语句执行完成,会返回受影响行数,有时候简单业务判断会依赖于这个返回值判断是否执行成功。
这种情况下,推荐使用非0判断,而不是去取具体返回的值。因为在有些数据库中,比如oracle,返回的-2也是表示成功的(也就是说返回-2、1或所有正整数都属于正常返回)

if(counts[i]==0) throw new BusException("XXXX异常了");

标签:map,顺序,不合理,list,update,死锁,id,ct
From: https://www.cnblogs.com/Nuwa/p/18151160

相关文章

  • 在vue2中按顺序调用多个接口,接收返回数据
    最近有一个点一个按钮调用多个接口,并且按顺序串行调用,根据后端返回的逻辑,区分接口之间的串行关系,前面的接口失败是否继续执行下面接口,有两种方式,都是循环调用接口,实现方式有点差别第一种reduce+promisevarresolves=[];callAPI(obj){returnnewPromise((resolve,reject......
  • 顺序表
    数据结构顺序表练习题1.已知一个顺序表L,其中的元素递增有序排列,设计一个算法,插入一个元素x(x为int型)后保持该顺序表仍然递增有序排列(假设插入操作总能成功)。/*****************************************************************************************filename......
  • 每月做账先后顺序
    每月做账先后顺序。·1、收付款分录:根据银行回单做。·2、销售分录:根据销售发票和出库单做(当月开具的发票)。·3、采购分录:根据采购发票和入库单做(采购提供的发票)。·4、费用分录:根据费用发票做(办公费、差旅费、推广费、咨询费等)。·5、计提工资、社保、公积金:根据工资表、社......
  • JMeter组件的执行顺序和作用域
    组件介绍测试计划:jmeter的起点和容器线程组:代表一定的虚拟用户取样器:发送请求的最小单元逻辑控制器:控制组件的执行顺序前置处理器:在请求之前的操作后置处理器:在请求之后的操作断言:判断请求是否符合预期定时器:是否延迟或间隔发送请求配置元件:请求期的配置信息监听器:负责......
  • GreatSQL 死锁案例分析
    1.背景概述客户业务发生死锁的报错,根据业务程序日志及业务流程,发现造成死锁的原因是:事务1delete+insert,事务2delete+insert2个事务交替执行导致的死锁;由于GAP锁阻塞了插入意向锁,并且当delete的数据存在时死锁不会发生,当delete的数据不存在时,会发生死锁。2.问题复现本......
  • 栈1: 栈的顺序存储
    栈:顺序存储栈是一种先进后出(FirstInLastOut,FILO)的数据结构如果你将两个元素压入栈,先加入的元素将在后加入的元素之后出栈栈顶元素值为null(未满时)定义栈的数据结构#defineMAX_SIZE1024//利用数组模拟栈的顺序存储typedefstructsqStack{void*data[MA......
  • 死锁
    多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致俩个或者多个线程都在等待对方释放资源。packagesyn;publicclassDeadLock{publicstaticvoidmain(String[]args){Makeups1=newMakeup(0,"小明");Makeups2=new......
  • 第十六章——处理锁、阻塞和死锁(3)——使用SQLServer Profiler侦测死锁
    前言:作为DBA,可能经常会遇到有同事或者客户反映经常发生死锁,影响了系统的使用。此时,你需要尽快侦测和处理这类问题。死锁是当两个或者以上的事务互相阻塞引起的。在这种情况下两个事务会无限期地等待对方释放资源以便操作。下面是死锁的示意图: 本文将使用SQLServerProfi......
  • 【Go】02.顺序编程
    类型基础类型类型关键字布尔类型bool整型int8、byte、int16、int、uint、uintptr浮点类型float32、float64复数类型complex64、complex128字符串string字符类型rune错误类型error复合类型类型关键字指针pointer数组array......
  • 图解 SQL 执行顺序,通俗易懂!
      数据的关联过程from&join&wheregroupbyhaving&whereselectorderbylimit这是一条标准的查询语句: 这是我们实际上SQL执行顺序:我们先执行from,join来确定表之间的连接关系,得到初步的数据where对数据进行普通的初步的筛选groupby分组各组分别执行havi......