天天看點

HikariCP連接配接池:Possibly consider using a shorter maxLifetime value.

錯誤分析、定位

在項目中使用到了資料庫叢集,使用時發現項目運作并且沒有操作資料庫一段時間之後再次操作資料庫就會控制台出現以下報錯。

HikariCP連接配接池:Possibly consider using a shorter maxLifetime value.
HikariCP連接配接池:Possibly consider using a shorter maxLifetime value.
Failed to validate connection [email protected] (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
           

項目整合mysql叢集的時候使用的是苞米豆開發的動态資料源工具。

<!--動态資料源,提供@DS注解友善使用動态資料源-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>2.5.4</version>
        </dependency>
           

看日志大概知道是連接配接池的錯誤,使用的是hikari資料庫連接配接池。之前隻知道druid沒了解過hikari,在整合動态資料源工具的時候也沒有配置相關的連接配接池。報錯内容裡建議我們使用小一些的maxLifetime值,是以很可能這個報錯的原因是連接配接池的參數maxLifetime大于資料庫的連接配接lifetime,連接配接池裡的連接配接沒過期,但資料庫那邊已經過期了,然後 operations after connection closed了。

mysql連接配接逾時參數

mysql有關連接配接逾時的參數有兩個,一個是interactive_timeout 另一個是wait_timeout ,機關都是s

使用以下語句檢視

HikariCP連接配接池:Possibly consider using a shorter maxLifetime value.

這兩個參數預設都是28800s,即8個小時;

其中interactive_timeout指的是mysql在關閉一個互動的連接配接之前所要等待的秒數 ;wait_timeout指的是mysql在關閉一個非互動的連接配接之前所要等待的秒數。通過mysql用戶端連接配接資料庫是互動式連接配接,通過jdbc連接配接資料庫是非互動式連接配接。

HikariCP連接配接逾時參數

Druid和HikariCP是處于活躍狀态資料庫連接配接池,據說更簡單更快。但本文關注其連接配接逾時參數。

HikariCP連接配接池:Possibly consider using a shorter maxLifetime value.
HikariCP連接配接池:Possibly consider using a shorter maxLifetime value.

檢視源碼以及官方文檔得知

CONNECTION_TIMEOUT 是等待來自池的連接配接的最大毫秒數,預設30000ms,也就是30秒。至少是250 ms。

IDLE_TIMEOUT是連接配接允許在池中閑置的最長時間,預設600000ms,也就是10分鐘。這個值至少是10 秒,0代表無限。這個值隻在設定了minimumIdle 時才有效。

MAX_LIFETIME是池中連接配接最長生命周期,預設1800000 ms,也就是30分鐘。這個值至少是30 秒,0代表無限。

這些值都遠遠小于mysql的8小時

官方文檔對于maxlifetime提到

We strongly recommend setting this value, and it should be several seconds shorter than any database or infrastructure imposed connection time limit

強烈建議我們将這個值設定為資料庫連接配接逾時的值短幾秒。

逾時時間的機制

長時間沒有使用該Connection,Connection會被Mysql關閉(但不為null)。此時調用該Connection時就會抛出異常。

但是按道理來說,mysql那邊8小時過期,而資料庫連接配接池30分鐘,不應該會出現這種問題的,并且這種問題出現在幾十秒内。

使用指令檢視目前的連接配接

HikariCP連接配接池:Possibly consider using a shorter maxLifetime value.

發現存活着大量的連接配接,大約有三四十個,應該是之前程式啟動留下來的一些連接配接,mysql這邊并沒有關閉連接配接,是連接配接池的問題。

那還是hikari的配置問題,隻好按照提示試着改一下max-lifetime

如果沒有使用動态連接配接庫可以使用一下配置。

spring:
  datasource:
    hikari:
        max-lifetime: 300000
           

由于使用的是動态連接配接庫,是以這個hikari要在其配置下配置。

spring:
  datasource:
    dynamic:
    	datasource:
			hikari:
		    	max-lifetime: 300000
           

改為800000在閑置五分鐘後發送請求能夠穩定觸發報錯,而當設定為30000時,則再也沒出現錯誤了。此時mysql資料庫的wait_timeout還是為8小時。

将wait_timeout改為了300秒,即和以上的修改一樣,都是五分鐘。修改之後大量的連接配接消失了,然後重新開機項目的時候發現啟動時就有四個連接配接,他們的time值最大不超過300,超過了就會從0開始數起,這個值受max-lifetime制約,随着後面的請求次數增多,連接配接就增多,但最終固定在10個連接配接。這10個連接配接受的是hikari的最大線程數制約,由于沒有設定閑置等待時間以及閑置線程數,是以大概這個程式運作期間一直都是10個連接配接,而當我退出程式之後,超過time超過300的連接配接會直接消失,不再從0開始數起。

HikariCP連接配接池:Possibly consider using a shorter maxLifetime value.

這裡有個非常繞的一點就是wait_timeout和interactive_timeout 。

mysql有兩個級别的配置,一個是session,另一個是global。下買面這個語句預設是session級别:

如果要看mysql的global級别需要加global參數:

這兩個級别下一共有四個參數:

  • global級别的interactive_timeout
  • global級别的wait_timeout
  • session級别的interactive_timeout
  • session級别的wait_timeout

對于各個參數作用的詳細實驗過程可以看這個文章,但是看得實在讓人頭暈。這裡我總結一下,有一下幾條需要注意的:

  1. 在互動模式下,session級别的 interactive_timeout 繼承了global級别 interactive_timeout 。
  2. 在互動模式下,連接配接時長受interactive_timeout 影響。
  3. 在非互動模式下,session級别的wait_timeout ,繼承了global級别wait_timeout 。
  4. 在非互動模式下,連接配接時長受wait_timeout 影響。
  5. 在互動模式下,session級别的wait_timeout ,繼承了global級别interactive_timeout。
  6. global級别的值修改對後續的連接配接有效,session級别的值對目前連接配接有效。
  7. 相同等級下的參數互不影響。

有人一第五條得出結論:要修改wait_timeout還要同時修改global級别的interactive_timeout。

原因是他們一直檢視和修改session級别的配置,但是這并沒有什麼本文來說沒有什麼意義。

我們的目的是修改後續我們的連接配接池的連接配接(非互動模式)的逾時時間,也就是希望後續的session級别的wait_timeout的值是我們的設定的值。根據第三條規則可以知道,這時候我們設定global級别的wait_timeout就可以了。

了解清楚了mysql的timeout機制就不難明白,global級别的wait_timeout對應的就是maxlifetime,為了防止網絡延遲導緻maxlifetime沒過期時發送請求,到達mysql時wait_timeout剛好過期,是以要将maxlifetime要設定的比maxlifetime小幾秒。至于為何之前8小時的wait_timeout和30分鐘的maxlifetime會在大概5分鐘左右丢失連接配接,這個一時半會兒還弄不明白,先這麼配置先吧。