應用端連線MySQL資料庫報Communications link failure

張衝andy發表於2018-11-15

事情的起因:

某專案的開發同學突然Q我們組的某同學,要求我們調整MySQL的連線等待超時引數wait_timeout。要求我們從28800s調整到31536000s(也就是一年)

應用端測試環境的tomcat報錯日誌如下圖:
 

 

恩。報錯很明顯。這個問題百度後的解決方案大部分都是要求資料庫端更改連線等待超時時間。那麼這種解決方法是否可行呢?

遺憾的是,這是不可行的。

主要原因還是效能考量。Wait_timeout引數的含義是指MySQL將斷開指定時間內沒有任何操作的連線(Connection)。這個值會直接影響到MySQL資料庫的併發效能,因為Connection是由引數max_connections設定的。

在高併發場景中,由於我們的連線數已經封頂,過長的wait_timeout值會導致連線長時間不釋放(如像開發同學提出修改為一年)。如果開發人員沒有在程式碼中顯式關閉連線,或者使用連線池時沒有定義連線回收方式,那麼連線數將會隨時間遞增,最後達到引數max_connections的設定值後,報經典的1040錯誤,too many connections...

所以為了避免出現這種情況,DBAs的立場是非常明確的,絕對不允許輕易修改生產環境資料庫的引數。這個原因我後面給出。那麼現在我們繼續focus到眼前的問題。

既然不允許更改資料庫引數,但是問題還在,那麼如何定位問題的真正原因呢?這裡我將根據我在處理這個問題時的思路,引導大家掌握基本的排障方法。

 

問題定位:

連線出現問題,那麼肯定要涉及到兩個主要的部分:資料庫和webserver的應用連線池。由於出於對自己工作的過度自信(笑),所以針對webserver端我問了開發同學幾個問題:

1. 是否使用了連線池?如果不是,是否有在程式碼裡顯式關閉連線?

2. 連線池是否配置了連線自動回收機制(如tomcat的話可能要涉及removeAbandoned和removeAbandonedTimeout等引數)?

 

開發同學給出的答案是:

1. 確實使用了連線池(dbcp原生的)。

2. 經過和運維相關同學協調,發現tomcat裡確實缺少連線回收的引數。

 

解決方案:

OK,那麼我基本可以定位問題了,下面就是給出解決方案:

1. Tomcat增加removeAbandoned=true、removeAbandonedTimeout=60、testOnBorrow=true和validationQuery=select now()。目的是讓webserver的連線池自己在連線前先判斷資料庫側是否已經將連線釋放掉,如果釋放掉則會回收並重建連線。注:removeAbandoned和removeAbandonedTimeout兩個引數我在給出時略有猶豫,因為這兩個引數可能會導致連線在removeAbandonedTimeout所設定的時間內沒有處理完時,也會被連線池強制回收,從而導致請求沒有返回資料就斷開了。所以一般生產環境中不會設定這兩個引數,不過在得知是測試環境後,我決定先嚐試一下看是否能定位問題就是連線池。

2. 如果上述方法不能解決,那麼可以嘗試更換c3p0這樣的第三方連線池。

3. 如果更換c3p0還不能解決,那麼請檢查綜測環境裡的這臺機器是否開啟了防火牆,或者是否selinux的策略有問題。這樣做主要是我發現錯誤日誌中的連線斷開時間非常有規律。如下圖:

因此,可能是某種機制導致人為斷開連線。所以在前兩種方法都不能解決的情況下,可以嘗試使用tcpdump等工具抓包,檢查當前系統網路環境。

 

經過一番痛苦的嘗試,終於在更換了c3p0的第三方連線池後,一切都正常了。

 

後記:

這個小故事,我總結了下面幾點請大家借鑑:

1. 所有你看到的故障只是它在某一時間段的某一表象。那麼怎麼根據這些表象迅速定位問題呢?除了工作經驗的積累外,還要掌握把分散的知識點聯絡起來的能力。這在處理故障時會大有用處。

2. 開發人員不要輕易要求DBA修改資料庫。包括引數、資料等等等等。前文提到不允許輕易修改生產資料庫的引數,這也是我在每次分享時都要強調的。資料是一個公司的核心,資料庫是存放這一核心的工具。資料庫最大的特點是穩定,也最需要穩定。不管前端應用開發的多麼花裡胡哨,實現了多麼複雜的邏輯,如果資料庫沒有穩定支撐,而是在不斷變動,那麼應用只會出現展現資料不正確(錯誤資料)或者資料不一致(髒資料)的情況。所以煩請開發同學們對每次提給資料庫組的請求一定要慎之又慎。

能在資料庫之前就有解決方案的,就絕對不要放到資料庫上做。

3. 儘可能的多瞭解自己工作之外的世界。做技術要有刨根問底的精神,多瞭解些和本職工作無關的,甚至是其他人的本質工作,你會發現你的職業道路會越走越寬~

 

PS:

MySQL的Connection Pool和Thread Pool之間的關係

很多人會混淆這兩個概念。在one-thread-per-connection的傳統配置裡,連線和執行緒就是1對1的關係。但是在thread pool的概念提出後,這種情況就不再是這樣的了。這裡我用不是DBA就能看懂的語言簡單的解釋一下:

連線池實現在Client端。由於Client端頻繁的建立和釋放連線會增加請求的平均響應時間,因此Client端往往會預先建立一些連線,通過這些連線來完成針對資料庫的所有請求。在Client端請求繁忙時,還可以通過請求排隊機制,緩解資料庫併發壓力。

執行緒池實現在Server端(資料庫端)。執行緒池和連線池有點類似,MySQL也會線上程池中預先分配相應的執行緒資源。除了能完成像連線池一樣的功能,如執行緒複用、請求佇列等,還在邏輯上將one-thread-per-connection中的1對1的關係轉變為多對1。MySQL的執行緒池會分為多個group,每個group中會fork出一個或多個worker執行緒。對於Client端連線池發起的每個連線(socket連線),並不會獨佔執行緒池的一個worker執行緒,而是一個worker執行緒會處理多個連線。如下圖:

不知道這樣解釋,你明白了嗎?


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31383567/viewspace-2220266/,如需轉載,請註明出處,否則將追究法律責任。

相關文章