【疑难系列】 一个看起来是数据库死锁的问题

起因

周六,7:10,闹钟还没响,客户电话过来了。

“彬哥,我们XX平台XX功能导致数据库死锁了,上次某某上去看过,把死锁的sqlserver进程杀过,但还是出现这个问题,麻烦你看一下”

“...”

起床,嗽口,吃个西红柿当早餐,出门(家里没网)

经过

连接服务器,重现问题

问题是:

某功能,点击之后等啊等,等啊等,等死了都没等到响应

所以

上次某某上去看过

使用这句sql查询到有被锁的连接

-- 查询死锁
select    
    request_session_id spid,   
    OBJECT_NAME(resource_associated_entity_id) tableName    
from    
    sys.dm_tran_locks   
where    
    resource_type='OBJECT'

于是将查询出来的死锁进程杀掉——但结果没用

凡是这种线程问题,都可以上jstack

找到java进程id,上jstack工具查看

D:\Program Files\Java\jdk1.8\bin>jstack 15316 > jstatck.log

将日志文件jstatck.log,拷贝到本地打开查看,

"http-nio-8080-exec-25" #197 daemon prio=5 os_prio=0 tid=0x0000000041b70800 nid=0x1530 waiting on condition [0x000000005f67f000]
   java.lang.Thread.State: TIMED_WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x00000003c66f3d98> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
	at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
	at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:85)
	at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:31)
	at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

这种java库的线程不用看,看我们自己写的代码部分

"http-nio-8080-exec-19" #191 daemon prio=5 os_prio=0 tid=0x000000003d743800 nid=0xce0 runnable [0x000000005ee5b000]
   java.lang.Thread.State: RUNNABLE
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
	at java.net.SocketInputStream.read(SocketInputStream.java:171)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at org.apache.http.impl.io.SessionInputBufferImpl.streamRead(SessionInputBufferImpl.java:137)
	at org.apache.http.impl.io.SessionInputBufferImpl.fillBuffer(SessionInputBufferImpl.java:153)
	at org.apache.http.impl.io.SessionInputBufferImpl.readLine(SessionInputBufferImpl.java:282)
	at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:140)
	at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:57)
	at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259)
	at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:163)
	at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:153)
	at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:273)
	at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
	at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:254)
	at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
	at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
	at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
	at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
	at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
	at 我们公司的代码.RestHandler.httpExecute(RestHandler.java:50)
	at 我们公司的代码.RestHandler.operatorToXXZX(RestHandler.java:44)
	at 我们公司的代码.ShortTermForecastService.saveToCIMISS(ShortTermForecastService.java:334)
	...(其它省略)

在这里找到了与我们公司的代码相关的内容。

这里表明两个问题:

1. 说明这个线程正在运行,与上述“等啊等,等啊等”的现象描述是一致的(没有运行完的线程不就这样么)

2. 这里的代码在访问某个url,并且一直在等待对方的响应

找到了出问题的地方,就可以查看源代码分析了

private void saveToCIMISS(final Long fid) throws Exception {
        //... 省略上面代码
        RestHandler.operatorToXXZX(EnumXXZX.INS_SHORTTREMPRODUCT.getUrl(), EnumXXZX.INS_SHORTTREMPRODUCT.getInterfaceId(), param);
        //...省略下面代码
    }

然后看看是调用了哪个 url 导致,至此问题原因已找到!

结果及分析

为什么会有“死锁”sqlserver连接呢

其实这并不是什么“死锁”,只是正常的锁

上面这个线程执行过程,会使用事务,事务引进的锁——而因为在事务过程中产生了外部的http访问,且该http长时间没有响应,导致事务锁因此也长时间占用数据库。

所以,表面看起来是数据库“死锁”了

解决问题思路

凡是线程问题,都可以用jstack工具

其它

面试的时候,面试官问我

“你遇到过最难解决的问题,你是怎么解决的?”

“我特么都是问题解决了就忘记了,所以没啥印象”

不过,我是在心里说的

所以,对于别人问我的问题,我决定记录下来,免得将来忘记了

posted @ 2019-06-15 09:46  小彬  阅读(869)  评论(0编辑  收藏  举报