天天看點

追溯 MySQL Statement Cancellation Timer1. 背景2. TimerThread3. getCancelTimer4. getCancelTimer 的上遊調用5. 建立 CancelTask timeoutTask6. CancelTask 執行過程7. Connection 關閉時8. 總結 MySQL Statement Cancellation Timer 線程的流程9. 閱讀資料

文章目錄

  • 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

隊列任務的執行和排程;

追溯 MySQL Statement Cancellation Timer1. 背景2. TimerThread3. getCancelTimer4. getCancelTimer 的上遊調用5. 建立 CancelTask timeoutTask6. CancelTask 執行過程7. Connection 關閉時8. 總結 MySQL Statement Cancellation Timer 線程的流程9. 閱讀資料
  • 根據定位

    Timer.java:526

    位置的代碼, 目前狀态

    WAITING (on object monitor)

    , 表示目前的

    timer

    線程池為空, 正在等待新入駐;
  • 根據定位

    Timer.java:552

    位置的代碼, 目前狀态

    TIMED_WAITING (on object monitor)

    表示任務等待被激活;
追溯 MySQL Statement Cancellation Timer1. 背景2. TimerThread3. getCancelTimer4. getCancelTimer 的上遊調用5. 建立 CancelTask timeoutTask6. CancelTask 執行過程7. Connection 關閉時8. 總結 MySQL Statement Cancellation Timer 線程的流程9. 閱讀資料

3. getCancelTimer

根據線程名稱

MySQL Statement Cancellation Timer

繼續追溯, 在

com.mysql.jdbc.ConnectionImpl#getCancelTimer

方法中找到該

TimerThread

的建立(

cancelTimer

):

追溯 MySQL Statement Cancellation Timer1. 背景2. TimerThread3. getCancelTimer4. getCancelTimer 的上遊調用5. 建立 CancelTask timeoutTask6. CancelTask 執行過程7. Connection 關閉時8. 總結 MySQL Statement Cancellation Timer 線程的流程9. 閱讀資料

4. getCancelTimer 的上遊調用

主要是

mysql-connector-java-xxx.jar

中負責 sql 查詢的

Statement

追溯 MySQL Statement Cancellation Timer1. 背景2. TimerThread3. getCancelTimer4. getCancelTimer 的上遊調用5. 建立 CancelTask timeoutTask6. CancelTask 執行過程7. Connection 關閉時8. 總結 MySQL Statement Cancellation Timer 線程的流程9. 閱讀資料
追溯 MySQL Statement Cancellation Timer1. 背景2. TimerThread3. getCancelTimer4. getCancelTimer 的上遊調用5. 建立 CancelTask timeoutTask6. CancelTask 執行過程7. Connection 關閉時8. 總結 MySQL Statement Cancellation Timer 線程的流程9. 閱讀資料

5. 建立 CancelTask timeoutTask

com.mysql.jdbc.StatementImpl#executeQuery

方法中可以發現, 當啟用

queryTimeout

timeoutInMillis!=0

時, 在執行 sql 的時候就會建立一個

CancelTask

的線程來控制逾時; (後面那個

versionMeetsMinimum

是個版本判斷可以先忽略)

追溯 MySQL Statement Cancellation Timer1. 背景2. TimerThread3. getCancelTimer4. getCancelTimer 的上遊調用5. 建立 CancelTask timeoutTask6. CancelTask 執行過程7. Connection 關閉時8. 總結 MySQL Statement Cancellation Timer 線程的流程9. 閱讀資料

然後在項目的

application.yml

中發現配置

mybatis.configuration.default-statement-timeout: 5

, 是以

mybatis

在每次的資料庫查詢都會加上

queryTimeout

, 且該配置對全局 SQL 生效, 包括

insert

,

select

,

update

;

追溯 MySQL Statement Cancellation Timer1. 背景2. TimerThread3. getCancelTimer4. getCancelTimer 的上遊調用5. 建立 CancelTask timeoutTask6. CancelTask 執行過程7. Connection 關閉時8. 總結 MySQL Statement Cancellation Timer 線程的流程9. 閱讀資料

6. CancelTask 執行過程

com.mysql.jdbc.StatementImpl.CancelTask#run

方法中, 會另起一個線程, 判斷如果啟用了

queryTimeoutKillsConnection

的配置時, 會調用目前

Statement

對應的

Connection

裡的

realClose

方法;

追溯 MySQL Statement Cancellation Timer1. 背景2. TimerThread3. getCancelTimer4. getCancelTimer 的上遊調用5. 建立 CancelTask timeoutTask6. CancelTask 執行過程7. Connection 關閉時8. 總結 MySQL Statement Cancellation Timer 線程的流程9. 閱讀資料

realClose

方法裡發現會關閉

cancelTimer

線程;

追溯 MySQL Statement Cancellation Timer1. 背景2. TimerThread3. getCancelTimer4. getCancelTimer 的上遊調用5. 建立 CancelTask timeoutTask6. CancelTask 執行過程7. Connection 關閉時8. 總結 MySQL Statement Cancellation Timer 線程的流程9. 閱讀資料

7. Connection 關閉時

com.mysql.jdbc.ConnectionImpl#close

方法裡也會發現有

realClose

方法的調用, 即在連接配接關閉時也會處理

cancelTimer

的釋放

追溯 MySQL Statement Cancellation Timer1. 背景2. TimerThread3. getCancelTimer4. getCancelTimer 的上遊調用5. 建立 CancelTask timeoutTask6. CancelTask 執行過程7. Connection 關閉時8. 總結 MySQL Statement Cancellation Timer 線程的流程9. 閱讀資料

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

追溯 MySQL Statement Cancellation Timer1. 背景2. TimerThread3. getCancelTimer4. getCancelTimer 的上遊調用5. 建立 CancelTask timeoutTask6. CancelTask 執行過程7. Connection 關閉時8. 總結 MySQL Statement Cancellation Timer 線程的流程9. 閱讀資料
  • 設定了

    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. 閱讀資料

  1. 一次資料庫連接配接池優化的實踐剖析
  2. MySQL Statement Cancellation Timer問題