【疑难系列】 一个看起来是数据库死锁的问题
起因
周六,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工具
其它
面试的时候,面试官问我
“你遇到过最难解决的问题,你是怎么解决的?”
“我特么都是问题解决了就忘记了,所以没啥印象”
不过,我是在心里说的
所以,对于别人问我的问题,我决定记录下来,免得将来忘记了