Druid MySQL 連線池本地實踐

FunTester發表於2024-04-01

本來不打算寫這個題目的,因為 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 連線池提供了非常豐富的配置引數,可以根據實際需求進行定製化配置,下面列出了一些常用的配置項:

  1. 基本配置:
    • driverClassName: 資料庫驅動類名
    • url: 資料庫 URL 連線字串
    • username: 資料庫使用者名稱
    • password: 資料庫密碼
  2. 初始化配置:
    • initialSize: 初始化連線池時建立的連線數量,預設 0
    • maxActive: 連線池中可同時連線的最大的活動的連線數,預設 8
    • maxIdle: 連線池中最大的空閒的連線數,太大 may 會使系統稍慢,若有批次執行查詢請增大該值,預設 8
  3. 超時時間配置:
    • maxWait: 獲取連線時最大等待時間 (毫秒),超過則丟擲異常,小於 0 則無限等待,預設無限
    • timeBetweenEvictionRunsMillis: 物件被過期前的休息時間,用於檢測連線是否被佔用,避免因其他原因長時間佔用而不能被檢測並從而移除廢棄連線。預設 1 分鐘
    • minEvictableIdleTimeMillis: 連線在池中最小生存的時間,單位是毫秒,預設 30 分鐘
  4. 測試配置:
    • testWhileIdle: 是否在從連線池取連線時檢測連線有效性,預設 false,非常耗時
    • testOnBorrow: 是否在連線池中取出連線前進行檢測連線有效性,預設 true,建議設定為 false,效能更好
    • testOnReturn: 是否在連線池中歸還連線時檢測連線有效性,預設 false
  5. 空閒連線回收配置:
    • removeAbandoned: 是否允許連線池中連線被回收,預設 false
    • removeAbandonedTimeout: 應該回收過期連線的時間,單位為秒,預設 300
    • logAbandoned: 是否按指定時間輸出連線回收的記錄,預設 false
  6. 其他配置:
    • 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 自動化
  • 測試理論雞湯
  • 社群風采&影片合集
如果覺得我的文章對您有用,請隨意打賞。您的支援將鼓勵我繼續創作!
打賞支援
暫無回覆。

相關文章