天天看點

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

事情的起因:

某項目的開發同學突然Q我們組的某同學,要求我們調整MySQL的連接配接等待逾時參數wait_timeout。要求我們從28800s調整到31536000s(也就是一年)

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

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

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

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

主要原因還是性能考量。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的政策有問題。這樣做主要是我發現錯誤日志中的連接配接斷開時間非常有規律。如下圖:

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

是以,可能是某種機制導緻人為斷開連接配接。是以在前兩種方法都不能解決的情況下,可以嘗試使用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線程會處理多個連接配接。如下圖:

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

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