C3P0連接超時分析

背景知識

c3p0是一個開源的、基於Java JDBC 規範的連接池管理框架。
官網地址:[http://www.mchange.com/projects/c3p0/]

獲取連接的過程

  1. c3p0構造了一個Connection對象池。
  2. 在對象池中有空閒對象時或者沒有達到對象池最大數量時,獲取Connection都會成功返回。但是要注意,獲取到的Connection不一定是可用的(比如服務端MySQL進程掛起、或者服務器掉電等極端情況下)。
  3. 由於空閒的Connetion不一定是可用的,所以c3p0會啓用一個檢查線程池來檢查空閒對象的可用性。檢查時,會將該空閒對象加入一個idleCheckResources(Set類型);檢查完成後,再做出刪除操作。其實idleCheckResources就是unused(LinkedList)的一個淺層拷貝。空閒連接檢查就是依靠執行select 1 sql來驗證。
  4. 在我們獲取Connection對象後,會檢查獲取的Connection是否在idleCheckResources中,如果在則等待mytemplate.pool.timeout時間後,遞歸調用本方法。
  5. 由於第四步的檢查機制,導致一個嚴重的問題。假設對象池中現在有3個連接,而且都是空閒的。並且這個時候三個檢查線程都阻塞在select 1了(比如數據庫掛起、或者斷電、網絡延時),需要說明的是這種阻塞是無限期的,即使在執行statement設置了超時時間也是一直阻塞,何況c3p0源碼中並沒有設置超時時間。那麼這個時候,業務線程已經拿到了其中一個對象,那麼進行第4步的檢查時,就會導致無限遞歸方法的出現(這種情況就類似自旋鎖的死鎖問題了),阻塞住getConnection,最終棧溢出。

下圖描述了這一個過程:

那麼爲什麼對於設置了statement的超時時間沒有效果呢?這是因爲MySQL的statement超時機制決定的,其機制如下圖:

關鍵是第8步,同樣是要發送sql來取消執行,所以這種時候取消sql也會被阻塞住。根本原因是阻塞在了socket.read()上了,所以發送的取消sql同樣會面臨阻塞的問題。statement超時一般是爲了控制數據庫正常時候sql的超時執行的。對於數據庫異常時候的超時,應該使用mysql的socket超時。mysql的socket超時只能在啓動參數中配置,沒有api來提供配置功能。或者還自己來實現異步的檢測機制。

JDBC相關的超時機制詳解可以參考:http://www.cubrid.org/blog/dev-platform/understanding-jdbc-internals-and-timeout-configuration/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章