写点什么

Druid MySQL 连接池本地实践

作者:FunTester
  • 2024-04-01
    河北
  • 本文字数:3813 字

    阅读完需:约 13 分钟

本来不打算写这个题目的,因为 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. 基本配置:

  2. driverClassName: 数据库驱动类名

  3. url: 数据库 URL 连接字符串

  4. username: 数据库用户名

  5. password: 数据库密码

  6. 初始化配置:

  7. initialSize: 初始化连接池时创建的连接数量,默认 0

  8. maxActive: 连接池中可同时连接的最大的活动的连接数,默认 8

  9. maxIdle: 连接池中最大的空闲的连接数,太大 may 会使系统稍慢,若有批量执行查询请增大该值,默认 8

  10. 超时时间配置:

  11. maxWait: 获取连接时最大等待时间(毫秒),超过则抛出异常,小于 0 则无限等待,默认无限

  12. timeBetweenEvictionRunsMillis: 对象被过期前的休息时间,用于检测连接是否被占用,避免因其他原因长时间占用而不能被检测并从而移除废弃连接。默认 1 分钟

  13. minEvictableIdleTimeMillis: 连接在池中最小生存的时间,单位是毫秒,默认 30 分钟

  14. 测试配置:

  15. testWhileIdle: 是否在从连接池取连接时检测连接有效性,默认 false,非常耗时

  16. testOnBorrow: 是否在连接池中取出连接前进行检测连接有效性,默认 true,建议设置为 false,性能更好

  17. testOnReturn: 是否在连接池中归还连接时检测连接有效性,默认 false

  18. 空闲连接回收配置:

  19. removeAbandoned: 是否允许连接池中连接被回收,默认 false

  20. removeAbandonedTimeout: 应该回收过期连接的时间,单位为秒,默认 300

  21. logAbandoned: 是否按指定时间输出连接回收的记录,默认 false

  22. 其他配置:

  23. filters: 配置一些扩展插件,常用的有 stat(计算一些统计数据)、log4j(使用 log4j 记录连接池日志)、wall(用于防止 SQL 注入)等

  24. validationQuery: 用来检测连接是否有效的 sql,这个会在应用程序每次申请连接时执行,类似select 1

  25. 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} inited16:31:57:353 pool-2-thread-1 活跃连接数: 216:31:57:353 pool-2-thread-2 活跃连接数: 216:31:57:354 pool-2-thread-1 空闲连接数: 016:31:57:354 pool-2-thread-2 空闲连接数: 016:31:58:365 pool-2-thread-3 活跃连接数: 216:31:58:365 pool-2-thread-4 活跃连接数: 216:31:58:365 pool-2-thread-3 空闲连接数: 016:31:58:366 pool-2-thread-4 空闲连接数: 016:31:58:369 main 活跃连接数: 016:31:58:370 main 空闲连接数: 2
复制代码


如果你在设置中增加了 com.alibaba.druid.pool.DruidDataSourceFactory#PROP_MAXIDLE = "maxIdle";的话,控制台会提示:


main maxIdle is deprecated


该配置已经过期了。

发布于: 刚刚阅读数: 5
用户头像

FunTester

关注

公众号:FunTester,800篇原创,欢迎关注 2020-10-20 加入

Fun·BUG挖掘机·性能征服者·头顶锅盖·Tester

评论

发布
暂无评论
Druid MySQL连接池本地实践_FunTester_InfoQ写作社区