? ? ? ? HiKari源于日語“光”的意思,HiKariCP顧名思義就是 和光速一樣快,HiKariCP是數據庫連接池的一個后起之秀,號稱性能最好,穩定性也不錯,完美地PK掉其他連接池。這里提供一篇文章介紹主流Java數據庫連接池比較及前瞻,文中重點介紹了當前主流開源數據庫連接池(比如C3P0、DBCP、Tomcat Jdbc Pool、Druid和Hikaricp)的性能分析和功能比較,有一定的參考價值。
? ? ? ?回到本文正題,近期在計費生產系統上遇到一個很頭疼的問題——Hikaricp連接池獲取不到連接數而連接不到數據庫,導致請求失敗。每10筆交易會出現2筆,具體異常如下:
Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 932347ms.
at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:555) ~[HikariCP-2.4.7.jar:?]
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:188) ~[HikariCP-2.4.7.jar:?]
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:147) ~[HikariCP-2.4.7.jar:?]
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:99) ~[HikariCP-2.4.7.jar:?]
at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111) ~[spring-jdbc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77) ~[spring-jdbc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:82) ~[mybatis-spring-1.2.3.jar:1.2.3]
at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:68) ~[mybatis-spring-1.2.3.jar:1.2.3]
at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:315) ~[mybatis-3.3.0.jar:3.3.0]
at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:75) ~[mybatis-3.3.0.jar:3.3.0]
at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:61) ~[mybatis-3.3.0.jar:3.3.0]
at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:303) ~[mybatis-3.3.0.jar:3.3.0]
at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:154) ~[mybatis-3.3.0.jar:3.3.0]
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:102) ~[mybatis-3.3.0.jar:3.3.0]
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:82) ~[mybatis-3.3.0.jar:3.3.0]
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:120) ~[mybatis-3.3.0.jar:3.3.0]
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:113) ~[mybatis-3.3.0.jar:3.3.0]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.7.0_79]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[?:1.7.0_79]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.7.0_79]
at java.lang.reflect.Method.invoke(Method.java:606) ~[?:1.7.0_79]
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:386) ~[mybatis-spring-1.2.3.jar:1.2.3]
... 26 more
問題分析:
1、異常拋出時間每次都是在15分30秒左右,但該系統的并行環境并沒有此異常,生產環境的差異只是數據庫部署在不同的主機上;
2、訪問數據庫命令的網絡耗時正常,數據庫端收到連接請求,且該時段數據庫連接數并沒有耗盡;
3、數據庫管理員在異常時間段查詢得知,不存在慢sql語句,不存在不合理設置索引導致此異常。
繼續分析,只能從Hikaricp的配置著手:以下是出異常的數據源配置?
自此附上hikarecp各個配置的中文解釋:hikariCP連接池配置
<bean?id="dataSourcePubdba" class="com.zaxxer.hikari.HikariDataSource">
? ? ?<property name="driverClassName" value="${datasource.driver}">
? ? ?<property name="jdbcUrl" value="${datasource.jdbcUrl}">
? ? ?<property name="username" value="${datasource.user}">
? ? ?<property name="password" value="${datasource.pass.encrypt}">
? ? ?<property name="connectionTimeout" value="30000" />
? ? <property name="idleTimeout" value="60000" />
? ? ?<property name="maxLifetime" value="1800000" />
? ? ?<property name="maximumPoolSize" value="65" />
</bean>
后四個重要參數的解釋:
? ? 1)connectionTimeout:等待連接池分配連接的最大時長(毫秒),超過這個時長還沒可用的連接則發生SQLException, 缺省:30秒?
? ? 2)idleTimeout:此屬性控制允許連接在池中閑置的最長時間,此設置僅適用于minimumIdle設置為小于maximumPoolSize的情況 默認:600000(10minutes)
? ?2)maxLifetime:一個連接的生命時長(毫秒),超時而且沒被使用則被釋放(retired),缺省:30分鐘,建議設置比數據庫超時時長少30秒
3)maximumPoolSize:連接池中允許的最大連接數(包括空閑和正在使用的連接)。缺省值:10;
綜合以上分析:connectionTimeout采用默認值30s,應該不會有太大問題。maximumPoolSize根據業務量的設置,不會有太大問題。maxLifetime如果過長,且idleTimeout沒有時間限制時,會導致連接數很大,空閑連接一直得不到釋放,嚴重擠占資源,容易引起連接數不夠的問題。特別是maxLifetime如果大于數據庫超時時長,就會拋出數據庫連接異常,這也是本次生產問題所在:maxLifetime設置成30分鐘,超過了數據庫連接時長15分鐘,connectionTimeout為30秒,所以每次異常都是在15分30秒拋出,數據庫端已經收到Hikaricp連接請求,但是因網絡問題等因素,到達超時時長而沒有獲取到連接。idleTimeout只有在minimumIdle設置為小于maximumPoolSize的情況下才生效,而我沒有設置最小空閑連接數minimumIdle的值,minimumIdle默認是等于maximumPoolSize,此時idleTimeout不受限,空閑連接一直沒有得到回收,出于系統優化以及并發穩定性考慮,應該增加此配置。
優化后的配置:
<bean id="dataSourceTxndba" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close" >//對象銷毀前調用了close函數,這是必須的,否則對象離開后,數據庫資源還可用
? ? ? ?<property name="driverClassName" value="${datasource.driver}"></property>
? ? ? ?<property name="jdbcUrl" value="${datasource.jdbcUrl2}"></property>
? ? ? ?<property name="username" value="${datasource.user2}"></property>
? ? ? ?<property name="password" value="${datasource.pass2.encrypt}"></property>
? ? ? ?<property name="connectionTimeout" value="30000" />
? ? ? ?<property name="idleTimeout" value="60000" />
? ? ? ?<property name="maxLifetime" value="600000" />
? ? ? ?<property name="minimumIdle" value="10" />
? ? ? ?<property name="maximumPoolSize" value="65" />
? ? ? ?<property name="poolName" value="TxnDatabasePool" />
? ? ? ?<property name="leakDetectionThreshold" value="5000"/>
</bean>
相比較之前的配置,優化點如下:
?1、增加數據連接關閉方法,當從dataSource獲取的連接使用完成后,調用close方法,以避免數據源對象任然可用,造成連接泄露
?2、增加最小空閑連接數minimumIdle,根據業務場景,設置為10,小于maximumPoolSize值
3、連接空閑時間idleTimeout生效,根據業務請求重發頻率為40多秒,值可設為1分鐘,減少空閑連接占用,盡快釋放數據庫連接
4、連接生命周期maxLifetime值設為10分鐘,低于數據庫超時時長,盡快釋放數據庫無效連接
5、增加連接池的用戶定義名稱
6、開啟連接監測泄露leakDetectionThreshold方法,此屬性控制在記錄消息之前連接可能離開池的時間量,表明可能的連接泄漏。值代表連接被占用的泄露時間最低可接受值為5秒,不過此值的設定需要根據場景多次調試,如果真實泄露時間小幅度超過5秒,會引起warning,但不一定會導出數據不能入庫,因為該方法只是檢查,只有到達idleTimeout ,才會強制執行關閉連接。
運行結果:
?使用該配置,目前系統暫未發生數據庫連接異常現象,后續還會繼續跟蹤,以期獲得最優配置。