spring事务导致的一次死锁

2019-03-22  本文已影响0人  ZzzzZzzzz_36b7

1,现象简述:

        项目上线,serviceA.methodA()中调用serviceB.methodB(String schoolId)的方法。

        serviceA.methodA()核心业务更新学校信息记录,核心sql:

update table_school set xx=xx where id=xxx;

        serviceB.methodB(String schoolId)核心业务是将学校update记录加入数据库中,将update的信息异步同步给其他服务。再入库之前,要去table_school表中查询所有is_sync=1的学校id,根据此字段判断这个学校是否需要同步。is_sync=0,则不入库。

List<String> schoolIds = schoolDAO.findSchoolIds();  //select id from table_school  where is_sync=1; 

if(!schoolIds.contains(schoolId)){

    continue; //不处理

}

//入库

        结果:在获取schoolIds 时候,程序卡住。所有需要查询table_school表的sql全部阻塞。程序处于不可用状态。

2,分析原因:

        当时通过运维查看数据库进程,了解到:1,数据库阻塞;2,table_school阻塞导致;3通过debug信息定位到具体问题dao,确认问题sql(种种原因,运维无法定位到具体sql)是  select id from table_school  where is_sync=1 。

        最重要的是当时只知道是阻塞。没有信息表明是发生了死锁。而且因为经验不足,数据库性能本身就有问题等各种因素。也没往这个方向考虑。一直怀疑是数据库性能和一些未知的慢sql导致阻塞。(经验不足,考虑方向错的离谱=。=)。

3,解决问题:

        涉及到两个service的方法。查看methodA(),methodB()的事务设置。

        methodA()为PROPAGATION_REQUIRED,timeout_300

        methodB()为PROPAGATION_NOT_SUPPORTED

        这就是问题的关键了,methodA()方法在新建的事务中执行,调用methodB()时,methodB()将methodA()的事务挂起,并且以非事务执行。这样导致methodA()的update school一直没提交,methodB()又去select school,造成了死锁(互斥锁)。因为methodA()的事务被挂起,methodB()没有事务,也不会发生事务超时。日志只能看到很久之后,dbserver close connection 的日志。无法定位程序问题。

        最后将schoolIds 的获取放到methodA()中,并将schoolIds作为参数传入methodB()中解决。

4,总结:

        菜是原罪!

        其实这个问题总的来说,并不复杂。只要能定位到当时数据库发生死锁,查到造成死锁的sql,就能很容易想到互斥锁,查看事务。主要时第一次遇到这种问题,经验不足,真的想不到这层原因。

5 ,收获:

        spring事务传播机制了解的更深了。

        数据库共享锁(s),互斥锁(x)。

        查询数据库死锁,定位死锁sql的方法。

上一篇 下一篇

猜你喜欢

热点阅读