在一些項目中由於一些特殊原因仍然保留着顯示的獲取數據庫連接(Connection)、提交事務、回滾事務、關閉連接等操作;其中關閉連接是比較容易疏忽又比較難在前期發現的問題。
我是如何排查連接未關閉的問題的? 首先還是提出3W:
1.What?
數據庫連接是應用服務器和數據庫之間建立的tcp連接,在獲取連接並進行操作后需要手動關閉以釋放資源,就像是文件流一樣,資源是有限的。
2.Why?
連接不釋放會導致連接池無法回收連接,進而數據庫連接逐漸被占滿,直到超出數據庫設置的最大連接數而拒絕服務,顯而易見這是不能接受的;同時由於數據庫連接也是tcp連接,未釋放的連接會占用應用服務器和數據庫服務器之間的tcp連接,有些情況下會導致無法應對突發流量(已經沒有足夠多的tcp連接)。
3.How?
顯示的調用connection.close()關閉連接或者利用spring管理連接。
我的排查步驟
1.掃描代碼
由於是顯示的開啟連接和關閉連接,則可以通過掃描代碼文件,獲取每個java文件中開啟連接的次數和關閉連接的次數,如果關閉連接的次數<開啟連接的次數,則說明很可能這里的代碼未關閉連接,則可以進一步排查。
但是由於每個開發人員的代碼風格不一致,比如有些是connection有些是conn;有些在外部開啟了連接,在if-else內部各關閉了2次連接等情況,所以掃描代碼的方式可能不太准確。
2.數據庫層面排查
如果未關閉連接,則其事務就不會被提交;通過mysql提供的事務表和開啟performance_schema后的線程表即可定位未提交事務執行的sql,根據sql反向查找代碼,以定位問題所在。
下面通過一個測試程序來模擬
執行代碼,執行后,主線程等待
查看mysql事務表,得到thread_id(其實是processlist_id,見下圖)
根據processlist_id查詢performance_schema線程信息
根據thread_id查詢具體的sql
然后就是根據sql反向查找代碼了...