本來不打算寫這個題目的,因為 Druid
大多都是在 Spring
中使用的,它很多功能非常強大,但是對於 MySQL
效能測試中並不實用。但是由於特殊原因,還是得把這個拾起來。
在以前的效能測試的過程當中,我通常會採用 執行緒繫結連線
的方式進行測試,畢竟也用不到很多執行緒,再不濟我就用 common-pool2
自己寫一個。但是考慮到穩定性測試當中,持續時間非常久,自定義的功能缺少自愈能力,最終還是選擇了使用已有成熟的 MySQL
連線池工具,經過幾番對比,最後選擇了 Druid
。
Druid 簡介
Druid 連線池是阿里巴巴開源的資料庫連線池專案,為監控而生,內建強大的監控功能,且監控特性不影響效能。Druid 連線池功能強大,效能優越,使用佔比高,是一款優秀的資料庫連線池。
Druid 連線池的主要特點包括:
- 高效能: Druid 連線池採用了一系列效能最佳化策略,包括預先建立連線、連線池複用、有效的連線驗證等,以提供高效的資料庫連線獲取和釋放操作。
- 可靠性: Druid 連線池提供了多種故障處理機制,可以有效地應對各種異常情況,確保資料庫連線的可靠性。
- 可管理性: Druid 連線池提供了豐富的監控和統計功能,可以實時監控連線池的狀態、活動連線數、請求頻率、SQL 執行情況等,方便使用者進行管理和最佳化。
- 安全性: Druid 連線池內建了防火牆功能,可以有效地防止 SQL 注入攻擊,並提供審計功能,可以幫助使用者追蹤資料庫操作行為。
- 擴充套件性: Druid 連線池支援多種資料庫型別,並可以方便地擴充套件支援新的資料庫型別。
Druid 連線池的使用非常簡單,只需幾行程式碼即可配置和使用,是 Java 應用開發中不可多得的利器。
一個例子
static void main(String[] args) {
// 建立 Druid 資料來源
DruidDataSource dataSource = new DruidDataSource()
// 配置資料庫連線資訊
dataSource.setUrl("jdbc:mysql://localhost:3306/funtester")
dataSource.setUsername("root")
dataSource.setPassword("funtester")
// 獲取資料庫連線
Connection connection = dataSource.getConnection()
// 執行 SQL 語句
Statement statement = connection.createStatement()
def query = statement.executeQuery("select id, uid, create_time from record order by id desc limit 10;")
while (query.next()) {
println("id: ${query.getInt(1)}, uid: ${query.getInt(2)}, create_time: ${query.getTimestamp(3)}")
}
query.close()
// 關閉資料庫連線
statement.close()
connection.close()
}
控制檯列印資訊就不再展示了。
Druid 配置項
上面例子中我們採取先建立 com.alibaba.druid.pool.DruidDataSource
物件,然後進行配置項設定。我們還有一種語法,如下:
// 配置Druid連線池屬性
Properties properties = new Properties()
properties.put("driverClassName", "com.mysql.cj.jdbc.Driver")
properties.put("url", "jdbc:mysql://localhost:3306/funtester")
properties.put("username", "root")
properties.put("password", "funtester")
properties.put("maxActive", "2")
// 建立Druid連線池
dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties)
Druid 連線池提供了非常豐富的配置引數,可以根據實際需求進行定製化配置,下面列出了一些常用的配置項:
-
基本配置:
-
driverClassName
: 資料庫驅動類名 -
url
: 資料庫 URL 連線字串 -
username
: 資料庫使用者名稱 -
password
: 資料庫密碼
-
-
初始化配置:
-
initialSize
: 初始化連線池時建立的連線數量,預設 0 -
maxActive
: 連線池中可同時連線的最大的活動的連線數,預設 8 -
maxIdle
: 連線池中最大的空閒的連線數,太大 may 會使系統稍慢,若有批次執行查詢請增大該值,預設 8
-
-
超時時間配置:
-
maxWait
: 獲取連線時最大等待時間 (毫秒),超過則丟擲異常,小於 0 則無限等待,預設無限 -
timeBetweenEvictionRunsMillis
: 物件被過期前的休息時間,用於檢測連線是否被佔用,避免因其他原因長時間佔用而不能被檢測並從而移除廢棄連線。預設 1 分鐘 -
minEvictableIdleTimeMillis
: 連線在池中最小生存的時間,單位是毫秒,預設 30 分鐘
-
-
測試配置:
-
testWhileIdle
: 是否在從連線池取連線時檢測連線有效性,預設 false,非常耗時 -
testOnBorrow
: 是否在連線池中取出連線前進行檢測連線有效性,預設 true,建議設定為 false,效能更好 -
testOnReturn
: 是否在連線池中歸還連線時檢測連線有效性,預設 false
-
-
空閒連線回收配置:
-
removeAbandoned
: 是否允許連線池中連線被回收,預設 false -
removeAbandonedTimeout
: 應該回收過期連線的時間,單位為秒,預設 300 -
logAbandoned
: 是否按指定時間輸出連線回收的記錄,預設 false
-
-
其他配置:
-
filters
: 配置一些擴充套件外掛,常用的有 stat(計算一些統計資料)、log4j(使用 log4j 記錄連線池日誌)、wall(用於防止 SQL 注入) 等 -
validationQuery
: 用來檢測連線是否有效的 sql,這個會在應用程式每次申請連線時執行,類似select 1
-
accessToUnderlyingConnectionAllowed
: 是否允許訪問底層連線,true 則允許使用者獲取到物理連線,預設 false
-
以上是一些 Druid 連線池常用的配置引數,在配置時可以根據專案實際情況進行調整。比如對於長時間保持空閒狀態的應用可以將maxIdle
設定小一些,而對於併發量大的應用則需要將maxActive
設定大一些。配置合理的連線池引數有利於提升應用的效能和穩定性。
併發
在效能測試過程中少不了要對連線池併發獲取連線、歸還連線。下面是演示的例子:
import com.alibaba.druid.pool.DruidDataSource
import com.alibaba.druid.pool.DruidDataSourceFactory
import com.funtester.frame.SourceCode
import java.sql.Connection
import java.util.concurrent.CountDownLatch
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
class DruidConcurrencyDemo extends SourceCode {
private static DruidDataSource dataSource
static {
try {
// 配置Druid連線池屬性
Properties properties = new Properties()
properties.put(DruidDataSourceFactory.PROP_DRIVERCLASSNAME, "com.mysql.cj.jdbc.Driver")
properties.put(DruidDataSourceFactory.PROP_URL, "jdbc:mysql://localhost:3306/funtester")
properties.put(DruidDataSourceFactory.PROP_USERNAME, "root")
properties.put(DruidDataSourceFactory.PROP_PASSWORD, "funtester")
properties.put(DruidDataSourceFactory.PROP_MAXACTIVE, "10")
properties.put(DruidDataSourceFactory.PROP_INITIALSIZE, "3")
properties.put(DruidDataSourceFactory.PROP_MAXWAIT, "5000")
// 建立Druid連線池
dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties)
} catch (Exception e) {
e.printStackTrace()
}
}
static void main(String[] args) throws InterruptedException {
int threadCount = 4 // 模擬4個併發請求
CountDownLatch latch = new CountDownLatch(threadCount)
ExecutorService executorService = Executors.newFixedThreadPool(threadCount)
// 併發獲取連線
for (int i = 0; i < threadCount; i++) {
executorService.execute(() -> {
try {
Connection connection = dataSource.getConnection()
// 模擬一些操作
Thread.sleep(1000) // 模擬1秒的操作時間
output("活躍連線數: " + dataSource.getActiveCount())
output("空閒連線數: " + dataSource.getPoolingCount())
connection.close() // 關閉連線
} catch (Exception e) {
e.printStackTrace()
} finally {
latch.countDown() // 計數器減一
}
})
}
latch.await() // 等待所有執行緒執行完畢
executorService.shutdown() // 關閉執行緒池
// 獲取連線池狀態
output("活躍連線數: " + dataSource.getActiveCount())
output("空閒連線數: " + dataSource.getPoolingCount())
}
}
控制檯輸出:
16:31:55:819 pool-2-thread-3 {dataSource-1} inited
16:31:57:353 pool-2-thread-1 活躍連線數: 2
16:31:57:353 pool-2-thread-2 活躍連線數: 2
16:31:57:354 pool-2-thread-1 空閒連線數: 0
16:31:57:354 pool-2-thread-2 空閒連線數: 0
16:31:58:365 pool-2-thread-3 活躍連線數: 2
16:31:58:365 pool-2-thread-4 活躍連線數: 2
16:31:58:365 pool-2-thread-3 空閒連線數: 0
16:31:58:366 pool-2-thread-4 空閒連線數: 0
16:31:58:369 main 活躍連線數: 0
16:31:58:370 main 空閒連線數: 2
如果你在設定中增加了 com.alibaba.druid.pool.DruidDataSourceFactory#PROP_MAXIDLE = "maxIdle";
的話,控制檯會提示:
main maxIdle is deprecated
該配置已經過期了。
- 2021 年原創合集
- 2022 年原創合集
- 2023 年原創合集
- 介面功能測試專題
- 效能測試專題
- Java、Groovy、Go、Python
- 單元&白盒&工具合集
- 測試方案&BUG&爬蟲&UI 自動化
- 測試理論雞湯
- 社群風采&影片合集