【疑難系列】 一個看起來是數據庫死鎖的問題


起因

周六,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工具

其它

面試的時候,面試官問我

“你遇到過最難解決的問題,你是怎么解決的?”

“我特么都是問題解決了就忘記了,所以沒啥印象”

不過,我是在心里說的

所以,對於別人問我的問題,我決定記錄下來,免得將來忘記了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM