Hikari?數(shù)據(jù)庫(kù)連接池內(nèi)部源碼實(shí)現(xiàn)的小細(xì)節(jié)
Hikari 默認(rèn)幾個(gè)超時(shí)配置
連接創(chuàng)建超時(shí)時(shí)間 30s
private static final long CONNECTION_TIMEOUT = SECONDS.toMillis(30);
連接存活驗(yàn)證時(shí)間5s,這個(gè)時(shí)間就是驗(yàn)證時(shí)socketTimeout,驗(yàn)證之后恢復(fù)為0,
但是真正做數(shù)據(jù)查詢時(shí)默認(rèn)為0,表示永不超時(shí)
private static final long VALIDATION_TIMEOUT = SECONDS.toMillis(5);
連接空閑時(shí)間10分鐘
private static final long IDLE_TIMEOUT = MINUTES.toMillis(10);
連接最大存活時(shí)間30分鐘
private static final long MAX_LIFETIME = MINUTES.toMillis(30);
Hikari 連接池中默認(rèn)連接數(shù)量為10
默認(rèn)最小連接數(shù)等于最大相同未為10
private static final int DEFAULT_POOL_SIZE = 10;
Hikari通過(guò)CopyOnWriteArrayList保存所有的連接
com.zaxxer.hikari.util.ConcurrentBag#sharedList
線程無(wú)法獲取連接時(shí)通過(guò)SynchronousQueue實(shí)現(xiàn)公平阻塞等待
當(dāng)池的連接被用盡,Hikari通過(guò)SynchronousQueue
實(shí)現(xiàn)讓線程阻塞等待,并且采用的是公平鎖。
源碼見(jiàn)com.zaxxer.hikari.util.ConcurrentBag#handoffQueue
以及com.zaxxer.hikari.util.ConcurrentBag#ConcurrentBag
構(gòu)造方法
Hikari內(nèi)部有三個(gè)單線程的 線程池 對(duì)象
houseKeepingExecutorService
,addConnectionExecutor
,closeConnectionExecutor
分別是都只有一條線程的線程池,源碼見(jiàn):com.zaxxer.hikari.pool.HikariPool
。houseKeepingExecutorService
是一個(gè)ScheduledExecutorService
對(duì)象。池中每創(chuàng)建一個(gè)連接,就會(huì)被封裝成一個(gè)PoolEntry
對(duì)象,然后放在定時(shí)任務(wù)中,定時(shí)時(shí)間就是設(shè)置的max-lifetime
。只要到達(dá)這個(gè)時(shí)間就會(huì)采取軟驅(qū)逐的方式從池中移除。- 除此之外,
houseKeepingExecutorService
還用于每隔30s來(lái)檢查一次池中的空閑連接、最大連接情況,并通過(guò)調(diào)用異步創(chuàng)建連接、異步銷毀連接的方法來(lái)維護(hù)池中連接數(shù)的平衡。
何謂軟驅(qū)逐
- 如果這個(gè)連接正在被使用,則不立即關(guān)閉連接,但是通過(guò)PoolEntry對(duì)象中的private volatile boolean evict;字段來(lái)標(biāo)記為需要關(guān)閉。下次有線程來(lái)獲取到這個(gè)連接時(shí),發(fā)現(xiàn)evict=true則調(diào)用異步關(guān)閉方法。重新獲取池中其它的連接。
- 如果這個(gè)連接未被使用,則立即調(diào)用異步關(guān)閉連接的方法。
池中連接的創(chuàng)建,關(guān)閉,除了初始化時(shí)只同步創(chuàng)建1條,其它都是異步的。
- 創(chuàng)建連接通過(guò)
addConnectionExecutor
(ThreadPoolExecutor
)來(lái)完成,關(guān)閉連接通過(guò)closeConnectionExecutor
(ThreadPoolExecutor
)來(lái)完成. - 這兩個(gè)線程池都每個(gè)都只有一個(gè)線程。加上
houseKeepingExecutorService
,那么一個(gè)Hikari連接池會(huì)創(chuàng)建3條線程!?。?/li> - 可以通過(guò)啟動(dòng)參數(shù)
-Dcom.zaxxer.hikari.blockUntilFilled
和InitializationFailTimeout
單位ms,來(lái)讓Hikari啟動(dòng)時(shí)等待創(chuàng)建完成設(shè)置的最小連接數(shù)。默認(rèn)為false
一個(gè)connection本質(zhì)上就是一個(gè)socket連接
一個(gè)連接池中,有多少個(gè)connection連接則對(duì)應(yīng)多少個(gè) socket 對(duì)象與服務(wù)端的連接。
Hikari中會(huì)使用ThreadLocal來(lái)將連接綁定到線程
Hikari為了提高從池中獲取連接的性能,通過(guò)ThreadLocal來(lái)避免資源競(jìng)爭(zhēng),一個(gè)connection可以對(duì)應(yīng)多個(gè)Thread,一個(gè)Thread可以綁定多個(gè)connection,源碼見(jiàn)ConcurrentBag類中的成員變量private final ThreadLocal<List<Object>> threadList;
為什么ThreadLocal里面的泛型是List< Object >?
因?yàn)橐粋€(gè)connection可以在不同時(shí)期被多個(gè)線程使用,當(dāng)另一個(gè)線程綁定的connection正在被別的線程使用時(shí),就需要選擇其它沒(méi)有被使用的connection,新選擇的connection同樣需要綁定到這條線程,所以使用的是List來(lái)保存。
源碼見(jiàn):com.zaxxer.hikari.util.ConcurrentBag#borrow
什么時(shí)候向ThreadLocal里面保存connection?
當(dāng)調(diào)用連接池中connection釋放資源的時(shí)候回收,這里的connection實(shí)際上是Hikari實(shí)現(xiàn)的一個(gè)代理類(ProxyConnection
),封裝了JDBC 連接。
源碼見(jiàn):com.zaxxer.hikari.pool.ProxyConnection#close
Hikari如何做到連接的回收
通過(guò)ProxyConnection
代理類來(lái)實(shí)現(xiàn)。
源碼見(jiàn):com.zaxxer.hikari.pool.ProxyConnection#close
Hikari通過(guò)CAS樂(lè)觀鎖來(lái)控制連接當(dāng)前狀態(tài)
Hikari通過(guò)PoolEntry
來(lái)封裝Connection
,并通過(guò)private volatile int state
來(lái)記錄Connection當(dāng)前狀態(tài),主要有如下幾個(gè)枚舉值,并通過(guò)CAS樂(lè)觀鎖來(lái)維護(hù)這些狀態(tài),提高多線程之間獲取連接的性能.
public interface IConcurrentBagEntry{ int STATE_NOT_IN_USE = 0; int STATE_IN_USE = 1; int STATE_REMOVED = -1; int STATE_RESERVED = -2; boolean compareAndSet(int expectState, int newState); void setState(int newState); int getState(); }
獲取連接,對(duì)驗(yàn)證連接可用性的優(yōu)化
每次從池中獲取連接后,先判斷連接最后訪問(wèn)時(shí)間和當(dāng)前時(shí)間差,如果小于500ms,則直接認(rèn)為連接是可用的,避免向服務(wù)器發(fā)送驗(yàn)證的sql語(yǔ)句,提高連接池性能。
源碼見(jiàn):com.zaxxer.hikari.pool.HikariPool#getConnection(long hardTimeout)
if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > aliveBypassWindowMs && !isConnectionAlive(poolEntry.connection))) { closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE); timeout = hardTimeout - elapsedMillis(startTime); }
否則需要驗(yàn)證連接可用性:
如果配置了connection-test-query
則使用配置sql驗(yàn)證,否則使用數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序?qū)崿F(xiàn)的java.sql.Connection#isValid(int timeout)
方法驗(yàn)證。
源碼見(jiàn):com.zaxxer.hikari.pool.PoolBase#isConnectionAlive
。
如果連接不可用,會(huì)輸出警告日志
如果驗(yàn)證連接可用性過(guò)程,連接因?yàn)閿?shù)據(jù)庫(kù)wait_timeout
超時(shí)被服務(wù)端關(guān)閉,或者網(wǎng)絡(luò)異常,則會(huì)出現(xiàn)如下警告日志,
Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl@2e2ce118 (No operations allowed after connection closed.). Possibly consider using a shorter maxLifetime value.
解決方案參考我的另一篇博客:Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl問(wèn)題排查
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
關(guān)于在IDEA中SpringBoot項(xiàng)目中activiti工作流的使用詳解
這篇文章主要介紹了關(guān)于在IDEA中SpringBoot項(xiàng)目中activiti工作流的使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08springboot使JUL實(shí)現(xiàn)日志管理功能
這篇文章主要介紹了springboot使JUL實(shí)現(xiàn)日志管理功能,本文分步驟給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09看過(guò)就懂的java零拷貝及實(shí)現(xiàn)方式詳解
這篇文章主要為大家詳細(xì)的介紹了什么是零拷貝,傳統(tǒng)的IO執(zhí)行流程,零拷貝相關(guān)的知識(shí)點(diǎn)回顧,零拷貝實(shí)現(xiàn)的幾種方式及java提供的零拷貝方式相關(guān)內(nèi)容,有需要的朋友可以借鑒參考下2022-01-01Java底層基于鏈表實(shí)現(xiàn)集合和映射--集合Set操作詳解
這篇文章主要介紹了Java底層基于鏈表實(shí)現(xiàn)集合和映射集合Set操作,結(jié)合實(shí)例形式詳細(xì)分析了Java使用鏈表實(shí)現(xiàn)集合和映射相關(guān)原理、操作技巧與注意事項(xiàng),需要的朋友可以參考下2020-03-03java實(shí)現(xiàn)圖片上傳至本地實(shí)例詳解
我們給大家分享了關(guān)于java實(shí)現(xiàn)圖片上傳至本地的實(shí)例以及相關(guān)代碼,有需要的朋友參考下。2018-08-08RocketMQ生產(chǎn)者一個(gè)應(yīng)用不能發(fā)送多個(gè)NameServer消息解決
這篇文章主要為大家介紹了RocketMQ生產(chǎn)者一個(gè)應(yīng)用不能發(fā)送多個(gè)NameServer消息原因及解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11Java吃貨聯(lián)盟訂餐系統(tǒng)代碼實(shí)例
這篇文章主要介紹了Java訂餐系統(tǒng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04