高并發(fā)下 MySQL Statement Cancellation Timer 的線程數(shù)暴漲
問題描述
線上業(yè)務(wù)高峰期 CPU 飆升,抓取 thread dump 發(fā)現(xiàn) MySQL Statement Cancellation Timer 的線程數(shù)比較多,接收到線上預(yù)警,分析一下原因。業(yè)務(wù)高峰:
圖片
下面是一些可能相關(guān)的信息( mysql 驅(qū)動(dòng),db 連接池,orm 框架)
依賴信息:
- mysql-jdbc 8.0.24
- druid 1.2.8
- mybatis 3.4.6
環(huán)境配置信息
- druid 配置,全部都是默認(rèn)值
- mybatis 配置:
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setVfs(SpringBootVFS.class);
factory.setDataSource(dataSource);
//todo 省略其他配置
Configuration c = new Configuration();
c.setLogImpl(StdOutImpl.class);
c.setDefaultStatementTimeout(25000);
factory.setConfiguration(c);
return factory.getObject();
}
發(fā)生過程分析
- 找到該線程的創(chuàng)建的地方 NativeSession
圖片
- 引用關(guān)系如下
圖片
- 什么時(shí)候啟動(dòng) enableQueryTimeouts = true
圖片
- 默認(rèn)值是 true
圖片
- startQueryTime 的調(diào)用方 StatementImpl 的 executeQuery
圖片
- 可以發(fā)現(xiàn) timeOutInMillis 不為 0 的情況下,并且 enableQueryTimeouts = true 就會(huì)創(chuàng)建 CanalQueryTask 然后如果超時(shí)就會(huì)自動(dòng)調(diào)度
方法調(diào)用如下:com.mysql.cj.CancelQueryTaskImpl#run
圖片
處理方案
- 項(xiàng)目使用的是使用 alibaba druid
圖片
參考:https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8調(diào)用的是 setQueryTimeOut 方法,然后傳給 timeOutInMills
圖片
如果需要取消 CancelQueryTask 需要將 validationQueryTimeout 設(shè)置為 0
- 并且去掉 mybatis defalutStatemnetTimeOut 參數(shù)
圖片
- 如果這些都去掉可以通過 mysql 服務(wù)端 collection timeout 配置處理
mysql 服務(wù)器會(huì)有一個(gè)參數(shù) wait_timeout:mysql server 關(guān)閉連接之前,允許連接閑置多少秒。默認(rèn)是 28800,單位秒,即 8 個(gè)小時(shí)。
# 分別查看全局、會(huì)話變量值
show global VARIABLES like '%timeout%';
show VARIABLES like '%timeout%';
圖片
- druid 可以通過 testOnBorrow 和 testOnReturn、testWhileIdle分別在鏈接獲取,鏈接歸還的時(shí)候判斷是否有效。
圖片
復(fù)現(xiàn)和修復(fù)
測(cè)試代碼
- PushCallbackService.java
- CallbackLog.java
- DBTimerController.java
- MccClient.java
修復(fù)效果
現(xiàn)象 MySQL Statement Cancellation Timer的線程不再產(chǎn)生
圖片
thread dump 分析工具地址:https://fastthread.io/
參考資料
- https://segmentfault.com/a/1190000020162800
- https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_wait_timeout