文章來源:雲棲社區,經同意授權轉載
鏈接:https://yq.aliyun.com/articles/226984?spm=5176.8091938.0.0.nCksaV
錯誤解決記錄:
java druid 連接池頻繁初始化后導致的too many connection數據庫報錯。
修改/etc/my.cnf
#加大連接列表數量
max_connections = 2000
# 調整失效連接清理時長 縮短
wait_timeout=7200
interactive_timeout=7200
以上兩個配置項配合使用。
一、什么是too many connection
1、重要參數
max_user_connections: The maximum number of simultaneous connections permitted to any given MySQL user account
允許的每個用戶最大鏈接數,如果超過這個數值,則會報: ERROR 1203 (42000): User dba already has more than 'max_user_connections' active connections。
一般這樣的報錯只會出現在業務機器上,並不會在DB server層報錯,這樣的話DBA就無法真正感知到錯誤,MySQL也非常貼心的推出了一個status供DBA查看:Connection_errors_max_connections
<section class="135brush" style="margin: 10px 0px; padding: 15px 20px 15px 45px; font-size: 14px; line-height: 22.39px; outline: 0px; border-width: 0px; border-style: initial; border-color: currentcolor; color: rgb(62, 62, 62); vertical-align: baseline; box-sizing: border-box; background-image: url(" https:="" mmbiz.qlogo.cn="" mmbiz_jpg="" tibrg3aoijtvy5gucqkfy5hqooqnktqmcc1e2igtetiaodqfbqphxthjdmycxagsoko2flsvbtyh2tekiklw2vcg="" 0?wx_fmt="jpeg");" ="" background-position:="" 1%="" 5px;="" background-repeat:="" no-repeat;"=""> Connection_errors_max_connections : The number of connections refused because the server max_connections limit was reached.
細心的同學就會發現:那如果出現'max_user_connections' 的報錯,就無法發現啦,這塊目前我還沒找到對應status。
二、什么情況下會發生too many connection
1、slow query 引起
-
真正的slow:該query的確非常慢
-
偽裝的slow:該query本身並不慢,是受其它因素的影響導致
2、sleep 空連接引起
-
沒有任何query,只是sleep,這種情況一般是代碼里面沒有主動及時釋放鏈接導致。
三、實戰案例
1、sleep 空鏈接引起的TMC(too many connection簡稱)
原因
由於代碼沒有主動及時的釋放鏈接,那么在DB Server中存在大量的sleep鏈接,一旦超過max_connections則報錯。
解決方案
(1)遇到這樣的報錯,如果沒有及時解決,則會導致后面的業務都一直連不上數據庫,影響面很大。
(2)所以我們第一件事情必須是保護數據庫,kill掉這些sleep鏈接。關於kill這件事,又有很多技巧可以談:
-
如果是人工kill,這簡直無法完成這樣艱巨的任務,因為業務會時刻產生這樣的sleep鏈接,有無盡頭
-
如果自己寫腳本,沒秒去kill,當然可行。但是我們卻碰到過非常極端的情況,那就是MySQL無法響應你的kill請求。
-
所以,這里還有一個更加靠譜的方案就是:設置wait_timeout, 它會自動幫你完成這項龐大且艱巨的任務,且一定可以kill掉
(3)完成上面幾個步驟之后,只能保證你的數據庫不會被壓到,且你有機會登陸進去做一些管理事情,但要徹底解決還必須讓業務方處理這些sleep鏈接。
-
業務團隊排查沒有釋放鏈接的原因。
-
通常,如果可以,DBA協助業務方提供TMC期間top ip,讓業務方排查服務哪里異常。
(4)啟用thread_pool功能可能可以解決這個問題,但是由於種種原因沒有使用。
-
MySQL官方社區版不支持
-
無法解決slow query引起的TMC
-
可能因為該組件導致其本身的問題
2、 slow query 引起的TMC
(1)先來說說真正的slow query
一般這種情況,也非常清晰明了,找到它,優化它,當然前提是你的數據庫還活着。
我們通常有SQL防火牆保護,大大降低了這樣的風險。預知SQL防火牆為何物,且聽下回分享。
(2)偽裝的slow query
好了,終於開始介紹這種最難的故障場景。
難點就是:因為它不是真正的slow,優化點難以尋找,所謂對症下葯,就是要找到對應的症狀,這也是難點所在。
廢話不多說,這里介紹下前一段時間遇到的一次真實的案例。
-
too many connection error
-
threads_runnig 非常多
-
幾乎找不到有問題的query,沒有明顯慢的query
-
幾乎任何語句都變得非常慢
-
服務器IO壓力並不大

官方文檔的解釋我不多說,這里簡單介紹下自己的理解。
-
innodb_thread_concurrency : 進入innodb存儲引擎的線程數量,如果數量滿了,就要排隊
-
innodb_thread_sleep_delay : 排隊等候進入innoDB的時候需要睡眠多長時間
-
innodb_adaptive_max_sleep_delay : 設置一個自適應的最大睡眠時間
-
innodb_concurrency_tickets: 一旦進入innoDB,就會獲取一個票據tickets,在票據期間可以隨意進入innoDB不需要排隊,如果用完了,理論上則要排隊(實測后發現並不是嚴格這套機制)
測試故障重現
表結構

set global innodb_thread_concurrency = 1; --方便模擬
測試用例,三個語句開始執行時間不差1秒


總結
1. 通過以上測試和結果分析得出:當query超過innodb_thread_concurrency時,其余query會等待,及時這樣的query非常快,也還是會等待,這就是所謂的偽裝的slow query。
2. 通過trx_started,now()分析得出:這些query直接的切換輪詢並不是真正意義上的平均公平分配,里面有一套自己的自適應算法,這里面我沒有深究下去,有興趣的同學可以繼續了解源碼。
3. 既然真正的原因找到,那么解決方案也就很快出來,那就是讓並發線程少一點,通過我們的omega平台可以很方便地得出這段時間哪些query和connect最多,那么協助業務一起溝通業務場景和優化方案,問題得到解決。