您好,登錄后才能下訂單哦!
Long long ago,there was a story about the Arguments between DBAs and the Developers...
這個故事,并沒有童話故事里王子和公主幸福的各種浪。那么就隨我,揭示故事發生的原因。
事情的起因:
某項目的開發同學突然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線程會處理多個連接。如下圖:
不知道這樣解釋,你明白了嗎?
Perry.Zhang
01.26.2016
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。