美文网首页
数据库读写模式引发应用和数据库死锁问题排查

数据库读写模式引发应用和数据库死锁问题排查

作者: 蒹葭残辉 | 来源:发表于2021-08-22 22:05 被阅读0次

说下问题背景:

某日,在公司测试环境,修改完一段代码运行后,频繁遇到接口卡住的问题,且迟迟没有响应,通过jstack打印出堆栈,发现没有任何用户线程的状态是WATING状态或是BLOCKED状态,则说明线程间没有死锁或活锁状态。

开始分析:

通过jstack日志分析出了一段可疑的线程,虽然他是RUNNABLE活跃状态,但其实是在进行IO流读写操作。熟悉操作系统的应该知道:IO读写操作时,线程在操作系统级别是休眠状态,但JVM级别线程状态仍然是RUNNABLE状态。

顺便附上线程在操作系统级别的状态和在JVM级别的状态持续图:

操作系统级别:


image.png

JVM级别:


image.png

我们再来看一下对应的线程堆栈:


image.png

从堆栈上可以看出,线程正在请求数据库,但数据库迟迟没有响应,处于IO阻塞状态,此时线程状态处于RUNNABLE,验证了我们之前的说法。

数据库查询操作为什么会迟迟没有响应?

数据库没有响应的问题一般的以下几种场景(可能罗列不全):
1、使用了悲观锁机制查询,如行锁,表锁等机制等待锁释放。
2、数据库读写机制,比如sqlserver读写机制有:读已提交,读快照等方式。
3、需要索引的数据量太大,需要很长时间才能响应。

通过分析代码1、3项基本可以直接排除,那么很可能就是读写机制出了问题。

对应的数据库是sqlserver,我们可以使用命令:

DBCC Useroptions

查询读写模式。


image.png

发现隔离模式是读已提交。读已提交就是说:我们要查询的数据如果被其他事务修改,则必须要等待事务释放后数据库才会响应。
通过观察代码发现,我们应用在修改了这条记录后事务并没有立即提交,而是又新开启了一个小的事务,再次去查询这条记录。最终导致外层事物修改后没有提交,内嵌事务查询在等待外层事务提交。而这是同一个线程,外层事务永远不会提交了,内嵌事务也永远等不来了,引发死锁血案。

解决方案,修改数据库的隔离级别吧,可以改成读快照的方式,这样如果事务没有提交,将会读事务开启前的数据,不会等待外层事务释放。

修改快照方式代码如下:

ALTER DATABASE [databaseName] SET SINGLE_USER WITH ROLLBACK IMMEDIATE ;
ALTER DATABASE [databaseName] SET ALLOW_SNAPSHOT_ISOLATION ON
ALTER DATABASE [databaseName] SET READ_COMMITTED_SNAPSHOT ON;
ALTER DATABASE [databaseName] SET MULTI_USER;

再次验证隔离级别是否成功:


image.png

相关文章

网友评论

      本文标题:数据库读写模式引发应用和数据库死锁问题排查

      本文链接:https://www.haomeiwen.com/subject/apihiltx.html