追溯 MySQL Statement Cancellation Timer

原文html

1. 背景

jstack 的內容中能夠看到如下的 MySQL Statement Cancellation Timer 守護線程, 在業務高峯期的時候會出現大量的這類守護線程, 由此追溯該線程的生命週期過程;java

"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.TimerThreadTimer.java 文件裏的一個內部類, 主要負責 Timer 隊列任務的執行和調度;mysql

  • 根據定位 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):sql

4. getCancelTimer 的上游調用

主要是 mysql-connector-java-xxx.jar 中負責 sql 查詢的 Statement數據庫

5. 建立 CancelTask timeoutTask

com.mysql.jdbc.StatementImpl#executeQuery 方法中能夠發現, 當啓用 queryTimeouttimeoutInMillis!=0 時, 在執行 sql 的時候就會建立一個 CancelTask 的線程來控制超時; (後面那個 versionMeetsMinimum 是個版本判斷能夠先忽略)segmentfault

而後在項目的 application.yml 中發現配置 mybatis.configuration.default-statement-timeout: 5, 因此 mybatis 在每次的數據庫查詢都會加上 queryTimeout, 且該配置對全局 SQL 生效, 包括 insert, select, update;mybatis

6. CancelTask 執行過程

com.mysql.jdbc.StatementImpl.CancelTask#run 方法中, 會另起一個線程, 判斷若是啓用了 queryTimeoutKillsConnection 的配置時, 會調用當前 Statement 對應的 Connection 裏的 realClose 方法;app

realClose 方法裏發現會關閉 cancelTimer 線程;oop

7. Connection 關閉時

com.mysql.jdbc.ConnectionImpl#close 方法裏也會發現有 realClose 方法的調用, 即在鏈接關閉時也會處理 cancelTimer 的釋放優化

8. 總結 MySQL Statement Cancellation Timer 線程的流程

設置了 queryTimeout 會使 jdbc driver 在每次查詢數據庫時新建 CancelTask(timeoutTask對象) 線程來處理超時, 並使用 CancelTimer(在 ConnectionImpl類中) 來進行調度;
若是 SQL 查詢超時了, 則會在 timeoutTaskrun 方法裏調用 com.mysql.jdbc.ConnectionImpl#realClose 來釋放 CancelTimer;
若是 Connection 正常關閉 close 時, 也會調用 com.mysql.jdbc.ConnectionImpl#realClose 來釋放 CancelTimer;

9. 閱讀資料

  1. 一次數據庫鏈接池優化的實踐剖析
  2. MySQL Statement Cancellation Timer問題
相關文章
相關標籤/搜索