文章目錄
- 1. 背景
- 2. TimerThread
- 3. getCancelTimer
- 4. getCancelTimer 的上遊調用
- 5. 建立 CancelTask timeoutTask
- 6. CancelTask 執行過程
- 7. Connection 關閉時
- 8. 總結 MySQL Statement Cancellation Timer 線程的流程
- 9. 閱讀資料
1. 背景
在
jstack
的内容中可以看到以下的
MySQL Statement Cancellation Timer
守護線程, 在業務高峰期的時候會出現大量的這類守護線程, 由此追溯該線程的生命周期過程;
"MySQL Statement Cancellation Timer" #20647 daemon prio=5 os_prio=0 tid=0x00007f2d087e9800 nid=0xfb83 in Object.wait() [0x00007f2b4b45a000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.util.TimerThread.mainLoop(Timer.java:552)
- locked <0x00000005da147038> (a java.util.TaskQueue)
at java.util.TimerThread.run(Timer.java:505)
Locked ownable synchronizers:
- None
"MySQL Statement Cancellation Timer" #24138 daemon prio=5 os_prio=0 tid=0x00007f402802c800 nid=0x4cf64 in Object.wait() [0x00007f3e49453000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at java.util.TimerThread.mainLoop(Timer.java:526)
- locked <0x00000005f606cc60> (a java.util.TaskQueue)
at java.util.TimerThread.run(Timer.java:505)
Locked ownable synchronizers:
- None
2. TimerThread
java.util.TimerThread
是
Timer.java
檔案裡的一個内部類, 主要負責
Timer
隊列任務的執行和排程;
- 根據定位
位置的代碼, 目前狀态Timer.java:526
, 表示目前的WAITING (on object monitor)
線程池為空, 正在等待新入駐;timer
- 根據定位
位置的代碼, 目前狀态Timer.java:552
表示任務等待被激活;TIMED_WAITING (on object monitor)
3. getCancelTimer
根據線程名稱
MySQL Statement Cancellation Timer
繼續追溯, 在
com.mysql.jdbc.ConnectionImpl#getCancelTimer
方法中找到該
TimerThread
的建立(
cancelTimer
):
4. getCancelTimer 的上遊調用
主要是
mysql-connector-java-xxx.jar
中負責 sql 查詢的
Statement
5. 建立 CancelTask timeoutTask
在
com.mysql.jdbc.StatementImpl#executeQuery
方法中可以發現, 當啟用
queryTimeout
且
timeoutInMillis!=0
時, 在執行 sql 的時候就會建立一個
CancelTask
的線程來控制逾時; (後面那個
versionMeetsMinimum
是個版本判斷可以先忽略)
然後在項目的
application.yml
中發現配置
mybatis.configuration.default-statement-timeout: 5
, 是以
mybatis
在每次的資料庫查詢都會加上
queryTimeout
, 且該配置對全局 SQL 生效, 包括
insert
,
select
,
update
;
6. CancelTask 執行過程
在
com.mysql.jdbc.StatementImpl.CancelTask#run
方法中, 會另起一個線程, 判斷如果啟用了
queryTimeoutKillsConnection
的配置時, 會調用目前
Statement
對應的
Connection
裡的
realClose
方法;
在
realClose
方法裡發現會關閉
cancelTimer
線程;
7. Connection 關閉時
在
com.mysql.jdbc.ConnectionImpl#close
方法裡也會發現有
realClose
方法的調用, 即在連接配接關閉時也會處理
cancelTimer
的釋放
8. 總結 MySQL Statement Cancellation Timer 線程的流程
- 設定了
會使queryTimeout
在每次查詢資料庫時建立jdbc driver
(CancelTask
對象) 線程來處理逾時, 并使用timeoutTask
(在CancelTimer
類中) 來進行排程;ConnectionImpl
- 如果
查詢逾時了, 則會在SQL
的timeoutTask
方法裡調用run
來釋放com.mysql.jdbc.ConnectionImpl#realClose
;CancelTimer
- 如果
正常關閉Connection
時, 也會調用close
來釋放com.mysql.jdbc.ConnectionImpl#realClose
;CancelTimer
9. 閱讀資料
- 一次資料庫連接配接池優化的實踐剖析
- MySQL Statement Cancellation Timer問題