彈性資料庫連線池探活策略調研(二)——Druid

2023-09-07 12:00:31

前言

上一篇文章中,我們介紹了彈性資料庫連線失效的背景,並探討了HikariCP連線池探活策略的相關內容。在本文中,我們將會繼續探討另一個線上常用的連線池——Druid,併為您介紹如何在使用Druid時實現最佳實踐的彈性資料庫連線池探活策略。

Druid

Druid的版本迭代更新比較快,同時探活設定的引數也比較多,這導致即使是相同的引數在不同的版本中達到的效果也可能不一樣。但與探活相關的邏輯實現只存在原始碼裡的兩個函數裡, 我們先列舉一下跟Druid探活相關的引數,在具體看一下原始碼的實現對這些引數的使用。日後我們在開發中遇到設定探活不生效的情況下,可以看一下對應版本原始碼來判斷自己的探活是否設定正確。

下面是與Druid探活相關的引數:

引數名稱 說明 預設值
initialSize 初始化時建立物理連線的個數。初始化發生在顯示呼叫init方法,或者第一次getConnection時。 0
minIdle 最小連線池數量。 0
maxActive 最大連線池數量。 8
testOnBorrow 申請連線時執行validationQuery設定的SQL檢測連線是否有效,做了這個設定會降低效能。 false
testOnReturn 歸還連線時執行validationQuery檢測連線是否有效,做了這個設定會降低效能。 false
testWhileIdle 建議設定為true,不影響效能,並且保證安全性。在連線池中申請連線的時候檢測,如果空閒時間大於timeBetweenEvictionRunsMillis,執行validationQuery檢測連線是否有效。 大多數版本為True
timeBetweenEvictionRunsMillis 1) Destroy執行緒會檢測連線的間隔時間,每隔這個值的時間就會執行一次DestroyTask。 2) testWhileIdle的判斷依據,詳細看testWhileIdle屬性的說明。 大多數版本是1分鐘
keepAlive 連線池中的minIdle數量以內的連線,空閒時間超過minEvictableIdleTimeMillis,則會執行探活操作此引數在1.0.28以上的版本才支援 詳細說明參考官方檔案。 false
keepAliveBetweenTimeMillis 配合keepAlive使用在低版本不支援,如果空閒時間小於timeBetweenEvictionRunsMillis但大於keepAliveBetweenTimeMillis扔執行探活操作 大多數版本是2分鐘
validationQuery 用來檢測連線是否有效的sql,要求是一個查詢語句。 select 1
validationQueryTimeout 單位:秒,檢測連線是否有效的超時時間。底層呼叫jdbc Statement物件的void setQueryTimeout(int seconds)方法
minEvictableIdleTimeMillis 連線空閒時間大於該值時關閉空閒連線大於minIdle的連線,類似hikaricp的idleTimeout 30分鐘
maxEvictableIdleTimeMillis 連線空閒時間大於該值時不管minIdle都關閉該連線,類似hikaricp的maxlifetime(低版本不支援) 7小時

Druid的探活主要有以下兩個函數來實現:

  • com.alibaba.druid.pool.DruidDataSource#getConnectionDirect

getConnectionDirect是每次從連線池中取連線時會被呼叫的函數。我們從下面的程式碼中可以看出,如果testOnBorrow為true,則每次獲取連線之前都會檢測連線是否有效。如果testOnBorrow為false且testWhileIdle為true,則需要判斷連線的空閒時間是否超過timeBetweenEvictionRunsMillis設定的值,如果超過則進行探活檢測。失效的連線會被丟棄,並且會補充到連線池的minIdle數量。timeBetweenEvictionRunsMillis在大多數版本中的預設值為1分鐘。只要這個值設定的時間小於十分鐘,並且保證testWhileIdle開啟,就能保證拿不到閘道器關閉的失效連線。

在不支援keepalive的低版本中,只能依靠testOnBorrow或testWhileIdle來進行探活。建議設定testWhileIdle來進行探活。在高並行的場景下,這種方式的效能消耗會更小一些。

  • com.alibaba.druid.pool.DruidDataSource#shrink(boolean, boolean)

在下面的程式碼中我們可以看出,shrink方法是在DestroyTask執行緒的run方法中呼叫的,用於銷燬連線池中的連線。如果timeBetweenEvictionRunsMillis大於0,則每隔這個時間間隔就會呼叫destroyTask.run(boolean, boolean)方法,即執行shrink方法。

從上面的程式碼中可以看出,shrink方法會使用keepAlive引數。需要注意的是,在不同版本的Druid中,keepAlive引數的支援和實現邏輯可能不同。官方建議在使用keepAlive引數時,應該使用1.1.21以上的版本。儘管官方檔案中說明了空閒時間超過minEvictableIdleTimeMillis,就會執行探活操作,但是在高版本中,這個探活操作的執行時間也受到了keepAliveBetweenTimeMillis引數的影響。因此,在高版本中,如果想要正確地使用keepAlive引數,就需要了解其在具體版本中的實現邏輯。

下面程式碼是1.1.10和1.1.21版本中關於shrink方法的原始碼對比:

首先看一下1.1.10版本的原始碼,它首先會判斷連線空閒時間是否大於minEvictableIdleTimeMillis,如果是,則接下來進行第二步的判斷:是否是多於minIdle的空閒連線。如果是,就將這些連線加入到驅逐連線的陣列中,以便進行後續的驅逐操作。如果不是,就再次判斷連線空閒時間是否大於maxEvictableIdleTimeMillis,如果是,則將這些連線加入到驅逐連線的陣列中。如果也不是,則進行最後的判斷:是否開啟了keepAlive設定。如果開啟了,就將這些連線加入到保活連線陣列中,以進行後續的探活操作。

在1.1.21版本中,shrink方法的總體邏輯與1.1.10版本類似,但是新增了一個名為keepAliveBetweenTimeMillis的引數。這個引數決定了使用keepAlive進行探活的時間間隔,其預設值為2分鐘,keepalive開啟且空閒時間大於這個值會進行探活。

另一個不同點是,在進行探活操作時,1.1.10版本僅會關閉無效的連線,但1.1.21版本則更進一步,除了關閉無效連線外,還會自動新增連線以達到minIdle的最小連線數。

1.1.10 1.1.21

總結,druid的探活引數在1.0.28版本之前沒有定時的探活功能只能在每次拿到連線前進行檢測是否有效,建議設定testWhileIdle為true在高並行情況下不會太影響效能,如果對可用性要求高的可以開啟testOnBorrow,以在每次獲取連線時檢測連線的有效性。在高版本中可以用keepAlive引數對連線進行保活。針對線上使用Druid連線池的應用建議使用支援keepAlive的1.1.21或者更高版本。

JED設定模版:

Druid1.1.10

<propertyname="testWhileIdle"value="true"/>    
<propertyname="validationQuery"value="SELECT 1"/>    
<propertyname="timeBetweenEvictionRunsMillis"value="30000"/>    
<propertyname="minEvictableIdleTimeMillis"value="300000"/>
<propertyname="keepAlive"value=true/>

此版本支援keepAlive可以設定minEvictableIdleTimeMillis時間小於10分鐘,能夠高效的進行探活防止閘道器關閉連線。

Druid1.1.9

同1.1.10

Druid1.0.9

<propertyname="testWhileIdle"value="true"/>    
<propertyname="validationQuery"value="SELECT 1"/>    
<propertyname="timeBetweenEvictionRunsMillis"value="30000"/>    
<propertyname="minEvictableIdleTimeMillis"value="300000"/>

此版本不支援keepAlive只能在獲取連線物件的時候檢測,對可用性高的也可以開啟testOnBorrow。

作者:京東零售 王雷鑫

來源:京東雲開發者社群 轉載請註明來源