Druid MySQL連接池本地實踐

本來不打算寫這個題目的,因爲 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

該配置已經過期了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章