Java中大量閑置MySQL連接的解決方案
引言
大量閑置 MySQL 連接會(huì)直接導(dǎo)致 MySQL 連接數(shù)耗盡(Too many connections)、服務(wù)器資源(內(nèi)存/文件句柄)浪費(fèi)、數(shù)據(jù)庫(kù)響應(yīng)變慢等問(wèn)題。核心解決思路是:先緊急回收閑置連接→再定位根因→最后通過(guò)代碼/連接池/MySQL 配置從根本避免,以下是分步驟的實(shí)操方案。
一、閑置連接的核心危害與識(shí)別方法
1. 閑置連接的危害
- 占用 MySQL 全局連接數(shù)(由
max_connections限制),導(dǎo)致新請(qǐng)求無(wú)法獲取連接,拋出Too many connections; - 每個(gè) MySQL 連接占用約 100KB~1MB 內(nèi)存,大量閑置連接會(huì)耗盡數(shù)據(jù)庫(kù)服務(wù)器內(nèi)存;
- 閑置連接若長(zhǎng)期不釋放,可能因 MySQL 端
wait_timeout被強(qiáng)制斷開(kāi),導(dǎo)致 Java 代碼出現(xiàn)「閉連接異?!?。
2. 如何識(shí)別閑置連接
(1)MySQL 端查看(直接定位閑置連接)
執(zhí)行 SQL 查看所有連接狀態(tài),State = Sleep 代表閑置連接:
-- 查看所有連接(關(guān)注 State、Time、User、Host 列) show processlist; -- 統(tǒng)計(jì)閑置連接數(shù)(Sleep 狀態(tài)且閑置超 60 秒) SELECT COUNT(*) FROM information_schema.processlist WHERE STATE = 'Sleep' AND TIME > 60;
Time列:連接閑置的秒數(shù);State = Sleep:連接處于空閑狀態(tài),無(wú) SQL 執(zhí)行;- 正常閑置連接數(shù)應(yīng)遠(yuǎn)小于
max_connections(建議<20%)。
(2)Java 連接池監(jiān)控(定位應(yīng)用側(cè)問(wèn)題)
通過(guò)連接池自帶監(jiān)控(如 Druid 監(jiān)控頁(yè)、HikariCP 日志)查看:
- 「空閑連接數(shù)」:長(zhǎng)期遠(yuǎn)大于「活躍連接數(shù)」;
- 「連接泄露」:連接借出后未歸還(Druid 可直接定位泄露的代碼棧)。
二、閑置連接的核心根因
| 根因分類(lèi) | 具體場(chǎng)景 |
|---|---|
| 代碼層面(最常見(jiàn)) | 1. 未關(guān)閉連接(try 塊中獲取連接,未在 finally/try-with-resources 中 close); 2. 連接復(fù)用差(每次請(qǐng)求新建連接,未用連接池); 3. 連接泄露(連接借出后未歸還,如線(xiàn)程池持有連接不釋放); |
| 連接池配置不合理 | 1. 最小空閑連接(minimumIdle)設(shè)置過(guò)大,連接池長(zhǎng)期維持大量空閑連接;2. 空閑超時(shí)( idleTimeout)未配置/設(shè)置過(guò)長(zhǎng),閑置連接不回收;3. 最大連接數(shù)( maximumPoolSize)設(shè)置過(guò)大,超出業(yè)務(wù)實(shí)際需求; |
| MySQL 端配置 | wait_timeout 過(guò)大(默認(rèn) 8 小時(shí)),MySQL 不主動(dòng)斷開(kāi)閑置連接; |
| 連接池使用不當(dāng) | 1. 多數(shù)據(jù)源未隔離,連接池配置復(fù)用導(dǎo)致閑置; 2. 連接池單例失效,創(chuàng)建多個(gè)連接池實(shí)例; |
三、分步驟解決閑置連接問(wèn)題
步驟 1:緊急回收閑置連接(止損優(yōu)先)
若已出現(xiàn)連接耗盡/大量閑置,先快速回收,避免業(yè)務(wù)中斷:
-- 殺死閑置超 60 秒的 Sleep 連接(替換為實(shí)際閾值)
SELECT CONCAT('KILL ', id, ';') FROM information_schema.processlist
WHERE STATE = 'Sleep' AND TIME > 60 INTO OUTFILE '/tmp/kill_idle_connections.sql';
-- 執(zhí)行生成的 kill 語(yǔ)句(謹(jǐn)慎:避免殺死業(yè)務(wù)連接)
SOURCE /tmp/kill_idle_connections.sql;
注意:僅清理「確認(rèn)閑置」的連接(最好在非業(yè)務(wù)高峰期操作),避免誤殺活躍連接。
步驟 2:修復(fù)代碼層面的連接泄露
90% 的閑置連接問(wèn)題源于代碼未正確釋放連接,核心是「確保連接用完即還」,以下是錯(cuò)誤 vs 正確示例:
錯(cuò)誤示例(未關(guān)閉連接,導(dǎo)致連接泄露/閑置)
// 錯(cuò)誤:連接未關(guān)閉,借出后無(wú)法歸還到連接池,最終成為閑置/泄露連接
public void queryData() {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 從連接池獲取連接
conn = DataSourceUtils.getConnection(dataSource);
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT * FROM user");
// 業(yè)務(wù)邏輯...
} catch (SQLException e) {
e.printStackTrace();
}
// 未在 finally 中關(guān)閉連接!
}
正確示例 1:try-with-resources(推薦,自動(dòng)關(guān)閉)
Java 7+ 支持,實(shí)現(xiàn) AutoCloseable 的對(duì)象(Connection/Statement/ResultSet)會(huì)自動(dòng)關(guān)閉:
public void queryData() {
// try-with-resources 自動(dòng)關(guān)閉連接、Statement、ResultSet
try (Connection conn = DataSourceUtils.getConnection(dataSource);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM user")) {
// 業(yè)務(wù)邏輯...
} catch (SQLException e) {
e.printStackTrace();
}
// 無(wú)需手動(dòng) close,JVM 自動(dòng)釋放連接到連接池
}
正確示例 2:finally 手動(dòng)關(guān)閉(兼容低版本 Java)
public void queryData() {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
conn = DataSourceUtils.getConnection(dataSource);
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT * FROM user");
// 業(yè)務(wù)邏輯...
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 逆序關(guān)閉資源,避免內(nèi)存泄漏
if (rs != null) {
try { rs.close(); } catch (SQLException e) {}
}
if (stmt != null) {
try { stmt.close(); } catch (SQLException e) {}
}
if (conn != null) {
try {
// 歸還連接到連接池(而非真正關(guān)閉)
DataSourceUtils.releaseConnection(conn, dataSource);
} catch (SQLException e) {}
}
}
}
步驟 3:優(yōu)化連接池配置
Java 項(xiàng)目幾乎都用連接池(HikariCP 是 Spring Boot 默認(rèn),Druid 是阿里開(kāi)源),通過(guò)合理配置從根本減少閑置連接。通常修改的連接池配置:
- 降低最小空閑連接數(shù)(
minimumIdle=0) - 縮短空閑超時(shí)(
idleTimeout=60000,1 分鐘) - 暫時(shí)降低最大連接數(shù)(
maximumPoolSize)至業(yè)務(wù)實(shí)際需求
(1)HikariCP 最優(yōu)配置(Spring Boot 示例)
application.yml 配置:
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
hikari:
# 核心配置(重點(diǎn))
maximum-pool-size: 10 # 最大連接數(shù):按業(yè)務(wù)QPS調(diào)整(8核16G服務(wù)器建議10-20)
minimum-idle: 0 # 最小空閑連接:設(shè)為0(Hikari默認(rèn)),避免長(zhǎng)期閑置
idle-timeout: 60000 # 空閑超時(shí):1分鐘(閑置超1分鐘回收)
max-lifetime: 1800000 # 連接最大生命周期:30分鐘(避免連接長(zhǎng)期閑置)
connection-timeout: 3000 # 獲取連接超時(shí):3秒(超時(shí)則拋異常,避免等待無(wú)效連接)
# 輔助配置
pool-name: MyHikariPool # 連接池名稱(chēng),便于監(jiān)控
connection-test-query: SELECT 1 # 借連接時(shí)校驗(yàn)連接可用性(避免使用閉連接)
關(guān)鍵參數(shù)解釋:
minimum-idle: 0:HikariCP 會(huì)根據(jù)業(yè)務(wù)需求動(dòng)態(tài)創(chuàng)建/銷(xiāo)毀連接,無(wú)閑置連接堆積;idle-timeout: 60000:閑置 1 分鐘的連接直接回收,避免長(zhǎng)期占著連接池;max-lifetime: 1800000:強(qiáng)制回收使用超 30 分鐘的連接,避免 MySQL 端因wait_timeout斷開(kāi)。
(2)Druid 最優(yōu)配置(適合需要監(jiān)控的場(chǎng)景)
Druid 自帶連接泄露監(jiān)控,配置示例:
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
# 核心配置
max-active: 10 # 最大連接數(shù)(同Hikari maximum-pool-size)
min-idle: 0 # 最小空閑連接
max-wait: 3000 # 獲取連接超時(shí)
time-between-eviction-runs-millis: 60000 # 空閑連接檢測(cè)周期:1分鐘
min-evictable-idle-time-millis: 60000 # 閑置超1分鐘回收
max-evictable-idle-time-millis: 1800000 # 連接最大閑置時(shí)間:30分鐘
validation-query: SELECT 1
test-while-idle: true # 空閑時(shí)校驗(yàn)連接可用性
# 監(jiān)控配置(定位連接泄露)
filters: stat,wall,log4j2
web-stat-filter:
enabled: true
stat-view-servlet:
enabled: true
url-pattern: /druid/* # 訪(fǎng)問(wèn) http://ip:port/druid 查看連接池監(jiān)控
login-username: admin
login-password: admin
# 連接泄露監(jiān)控(關(guān)鍵)
remove-abandoned: true # 開(kāi)啟泄露連接回收
remove-abandoned-timeout: 300 # 連接借出超5分鐘未歸還,強(qiáng)制回收
log-abandoned: true # 打印泄露連接的代碼棧(便于定位)
步驟 4:適配 MySQL 端配置
調(diào)整 MySQL 配置,讓數(shù)據(jù)庫(kù)主動(dòng)清理閑置連接。
需重啟 MySQL:
# my.cnf / my.ini 配置 [mysqld] max_connections = 1000 # 全局最大連接數(shù)(按服務(wù)器配置調(diào)整) wait_timeout = 600 # 連接閑置超 10 分鐘,MySQL 主動(dòng)斷開(kāi)(建議 60~600 秒) interactive_timeout = 600 # 與 wait_timeout 保持一致(交互式連接超時(shí))
動(dòng)態(tài)生效(無(wú)需重啟):
SET GLOBAL wait_timeout = 600; SET GLOBAL interactive_timeout = 600;
注意:
wait_timeout需 ≤ 連接池的max-lifetime(如連接池設(shè) 30 分鐘,MySQL 設(shè) 10 分鐘),避免 MySQL 先斷開(kāi)連接,導(dǎo)致 Java 代碼使用閉連接。
步驟 5:監(jiān)控與預(yù)警
通過(guò)監(jiān)控提前發(fā)現(xiàn)閑置/泄露連接,避免問(wèn)題擴(kuò)大:
(1)連接池監(jiān)控
- Druid:訪(fǎng)問(wèn)
/druid監(jiān)控頁(yè),查看「活躍連接數(shù)」「空閑連接數(shù)」「連接泄露數(shù)」; - HikariCP:通過(guò)
HikariPoolMXBean暴露監(jiān)控指標(biāo),接入 Prometheus + Grafana:// 獲取 Hikari 監(jiān)控指標(biāo) HikariDataSource ds = (HikariDataSource) dataSource; HikariPoolMXBean poolMXBean = ds.getHikariPoolMXBean(); System.out.println("空閑連接數(shù):" + poolMXBean.getIdleConnections()); System.out.println("活躍連接數(shù):" + poolMXBean.getActiveConnections());
(2)MySQL 監(jiān)控
- 監(jiān)控
show processlist中Sleep連接數(shù),超過(guò)閾值(如 50)則告警; - 監(jiān)控
max_connections使用率,超過(guò) 80% 則告警。
(3)業(yè)務(wù)監(jiān)控
- 監(jiān)控「獲取連接耗時(shí)」:耗時(shí)突增可能是連接池耗盡/閑置連接過(guò)多;
- 監(jiān)控「SQL 執(zhí)行異?!梗喝?
Communications link failure(連接被 MySQL 斷開(kāi))。
四、一些最佳實(shí)踐(避免閑置連接的核心原則)
- 強(qiáng)制使用連接池:禁止手動(dòng)創(chuàng)建
DriverManager.getConnection()(每次新建連接,用完未關(guān)即閑置); - 連接池參數(shù)標(biāo)準(zhǔn)化:
- 最大連接數(shù):按「CPU 核心數(shù) × 2 + 磁盤(pán)數(shù)」或業(yè)務(wù) QPS 調(diào)整(如 8 核服務(wù)器設(shè) 10~20);
- 最小空閑連接:一律設(shè) 0(讓連接池動(dòng)態(tài)伸縮);
- 空閑超時(shí):1~5 分鐘,最大生命周期:30 分鐘;
- 代碼規(guī)范:所有數(shù)據(jù)庫(kù)操作必須用
try-with-resources或finally關(guān)閉連接; - 定期巡檢:每周查看 MySQL 連接狀態(tài)、連接池監(jiān)控,排查閑置/泄露連接;
- 壓測(cè)驗(yàn)證:上線(xiàn)前壓測(cè),驗(yàn)證連接池配置是否匹配業(yè)務(wù)峰值,避免閑置/耗盡。
總結(jié)
解決大量閑置 MySQL 連接的核心邏輯是:
- 緊急止損:手動(dòng)清理閑置連接 + 臨時(shí)調(diào)整連接池配置;
- 根因治理:修復(fù)代碼連接泄露 + 優(yōu)化連接池/MySQL 配置;
- 長(zhǎng)期預(yù)防:監(jiān)控預(yù)警 + 規(guī)范代碼/配置。
其中,代碼層面的連接釋放規(guī)范是最核心的環(huán)節(jié),連接池配置優(yōu)化是關(guān)鍵手段,兩者結(jié)合可從根本解決閑置連接問(wèn)題。
以上就是Java中大量閑置MySQL連接的解決方案的詳細(xì)內(nèi)容,更多關(guān)于Java大量閑置MySQL連接的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java之Spring AOP 實(shí)現(xiàn)用戶(hù)權(quán)限驗(yàn)證
本篇文章主要介紹了Java之Spring AOP 實(shí)現(xiàn)用戶(hù)權(quán)限驗(yàn)證,用戶(hù)登錄、權(quán)限管理這些是必不可少的業(yè)務(wù)邏輯,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-02-02
java布局管理之CardLayout簡(jiǎn)單實(shí)例
這篇文章主要為大家詳細(xì)介紹了java布局管理之CardLayout的簡(jiǎn)單實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03
SpringBoot配置外部靜態(tài)資源映射問(wèn)題
這篇文章主要介紹了SpringBoot配置外部靜態(tài)資源映射問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
springboot啟動(dòng)時(shí)運(yùn)行代碼詳解
在本篇內(nèi)容中我們給大家整理了關(guān)于在springboot啟動(dòng)時(shí)運(yùn)行代碼的詳細(xì)圖文步驟以及需要注意的地方講解,有興趣的朋友們學(xué)習(xí)下。2019-06-06
Spring5使用JSR 330標(biāo)準(zhǔn)注解的方法
從Spring3.0之后,除了Spring自帶的注解,我們也可以使用JSR330的標(biāo)準(zhǔn)注解,本文主要介紹了Spring5使用JSR 330標(biāo)準(zhǔn)注解,感興趣的可以了解一下2021-09-09
java實(shí)現(xiàn)文件切片上傳百度云+斷點(diǎn)續(xù)傳的方法
文件續(xù)傳在很多地方都可以用的到,本文主要介紹了java實(shí)現(xiàn)文件切片上傳百度云+斷點(diǎn)續(xù)傳的方法,?文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12
Logback日志基礎(chǔ)及自定義配置代碼實(shí)例
這篇文章主要介紹了Logback日志基礎(chǔ)及自定義配置代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09

