前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >为什么说druid是目前最好的数据库连接池?

为什么说druid是目前最好的数据库连接池?

作者头像
Java极客技术
发布于 2022-12-04 03:12:05
发布于 2022-12-04 03:12:05
1.1K01
代码可运行
举报
文章被收录于专栏:Java极客技术Java极客技术
运行总次数:1
代码可运行

一、介绍

数据库连接是一项非常关键的、有限的、昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。

记得之前做的一个项目,当时的应用程序配置的数据库连接池,最大允许的连接数是500,结果上线没多久,并发量直接上来了,导致大量的数据插入失败,当晚的心情可想而知~

从那一次事故之后,让我对应用程序的数据库连接数有了一次深刻的认识,为了防止再次栽跟头,之后特意抽了一个时间来编写程序测试案例,用于测试各个数据源连接池的稳定性!

话不多说,直接撸起来!

二、程序实例

熟悉 web 系统开发的同学,基本都知道,在 Java 生态中开源的常用数据库连接池有以下几种:

  • dbcpDBCP是一个依赖Jakarta commons-pool对象池机制的数据库连接池,DBCP可以直接的在应用程序中使用,Tomcat的数据源使用的就是DBCP
  • c3p0c3p0是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3jdbc2扩展规范说明的ConnectionStatement池的DataSources对象
  • druid:阿里出品,淘宝和支付宝专用数据库连接池,但它不仅仅是一个数据库连接池,它还包含一个ProxyDriver,一系列内置的JDBC组件库,一个SQL Parser。支持所有JDBC兼容的数据库,包括OracleMySqlDerbyPostgresqlSQL ServerH2等等。

今天我们就一起来对比一下,这三种数据源连接池的稳定性。

2.1、创建测试表

下面以 mysql 数据库为例,创建一个t_test表,方面后续进行插入数据操作。

代码语言:javascript
代码运行次数:0
运行
复制
CREATE TABLE t_test (
  id bigint(20) unsigned NOT NULL COMMENT '主键ID',
  name varchar(32) NOT NULL COMMENT '名称',
  PRIMARY KEY (id)
) ENGINE=InnoDB COMMENT='测试表';
2.2、 编写测试用例

dbcp为例,首先创建一个dbcp-jdbc.properties配置文件。

代码语言:javascript
代码运行次数:0
运行
复制
username=root
password=Hello@123456
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.31.200:3306/testdb?useUnicode=true&characterEncoding=UTF-8
initialSize=5
maxActive=1000
maxIdle=5
removeAbandoned=ture
removeAbandonedTimeout=20
logAbandoned=true
maxWait=100

接着,创建一个连接池工具DbcpJdbcUtil

代码语言:javascript
代码运行次数:0
运行
复制
public class DbcpJdbcUtil {
 
 private static final Logger logger = LoggerFactory.getLogger(DbcpJdbcUtil.class);
 
 /**jdbc配置文件*/
 private static Properties prop = new Properties(); 
 
 private static BasicDataSource dataSource = null;
 // 它是事务专用连接!
 private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
 
 static {
  classPathSourceRead();
 }

    private static void classPathSourceRead(){
     //读取指定位置的配置文档(读取class目录文件)
     try {
      logger.info("jdbc路径:" + SysConstants.getValue());
   prop.load(DbcpJdbcUtil.class.getClassLoader().getResourceAsStream(SysConstants.getValue()));
   logger.info("数据配置信息" + JSON.toJSONString(prop));
   logger.info("初始化默认jdbc配置文件成功!");
  } catch (Exception e) {
   logger.error("初始化默认jdbc文件失败!",e);
  }
    }
    
 /**
  * 从连接池获取数据源
  * @return
  * @throws Exception
  */
 public static BasicDataSource getDataSource() throws Exception {
  try {
   if (dataSource == null) {
    synchronized (DbcpJdbcUtil.class) {
     if (dataSource == null) {
      dataSource = new BasicDataSource();
      dataSource.setUsername(prop.getProperty("username"));
      dataSource.setPassword(prop.getProperty("password"));
      dataSource.setDriverClassName(prop.getProperty("driverClassName"));
      dataSource.setUrl(prop.getProperty("url"));
      dataSource.setInitialSize(Integer.valueOf(prop.getProperty("initialSize")));
      dataSource.setMaxActive(Integer.valueOf(prop.getProperty("maxActive")));
      dataSource.setMaxIdle(Integer.valueOf(prop.getProperty("maxIdle")));
      dataSource.setRemoveAbandoned(Boolean.valueOf(prop.getProperty("removeAbandoned")));
      dataSource.setRemoveAbandonedTimeout(Integer.valueOf(prop.getProperty("removeAbandonedTimeout")));
      dataSource.setLogAbandoned(Boolean.valueOf(prop.getProperty("logAbandoned")));
      dataSource.setMaxWait(Integer.valueOf(prop.getProperty("maxWait")));
     }
    }
   }
   return dataSource;
  } catch (Exception e) {
   logger.error("根据数据库名称获取数据库资源失败," , e);
   throw new Exception("根据数据库名称获取数据库资源失败");
  }
 }
 
 /**
  * 使用连接池返回一个连接对象
  * 
  * @return
  * @throws SQLException
  */
 public static Connection getConnection() throws Exception {
  try {
   Connection con = tl.get();
   // 当con不等于null,说明已经调用过beginTransaction(),表示开启了事务!
   if (con != null)
    return con;
   return getDataSource().getConnection();
  } catch (Exception e) {
   logger.error("获取数据库连接失败!", e);
   throw new SQLException("获取数据库连接失败!");
  }
 }
 
 /**
  * 开启事务 1. 获取一个Connection,设置它的setAutoComnmit(false) 
  * 2. 还要保证dao中使用的连接是我们刚刚创建的! -------------- 
  * 3. 创建一个Connection,设置为手动提交 
  * 4. 把这个Connection给dao用! 
  * 5. 还要让commitTransaction或rollbackTransaction可以获取到!
  * 
  * @throws SQLException
  */
 public static void beginTransaction() throws Exception {
  try {
   Connection con = tl.get();
   if (con != null) {
    con.close();
    tl.remove();
    //throw new SQLException("已经开启了事务,就不要重复开启了!");
   }
   con = getConnection();
   con.setAutoCommit(false);
   tl.set(con);
  } catch (Exception e) {
   logger.error("数据库事物开启失败!", e);
   throw new SQLException("数据库事物开启失败!");
  }
 }

 /**
  * 提交事务 1. 获取beginTransaction提供的Connection,然后调用commit方法
  * 
  * @throws SQLException
  */
 public static void commitTransaction() throws SQLException {
  Connection con = tl.get();
  try {
   if (con == null)
    throw new SQLException("还没有开启事务,不能提交!");
   con.commit();
  } catch (Exception e) {
   logger.error("数据库事物提交失败!", e);
   throw new SQLException("数据库事物提交失败!");
  } finally {
   if (con != null) {
    con.close();
   }
   tl.remove();
  }
 }
 
 /**
  * 回滚事务 1. 获取beginTransaction提供的Connection,然后调用rollback方法
  * 
  * @throws SQLException
  */
 public static void rollbackTransaction() throws SQLException {
  Connection con = tl.get();
  try {
   if (con == null)
    throw new SQLException("还没有开启事务,不能回滚!");
   con.rollback();
  } catch (Exception e) {
   logger.error("数据库事物回滚失败!", e);
   throw new SQLException("数据库事物回滚失败!");
  } finally {
   if (con != null) {
    con.close();
   }
   tl.remove();
  }
 }
 
 /**
  * 释放连接 
  * @param connection
  * @throws SQLException
  */
 public static void releaseConnection(Connection connection) throws SQLException {
  try {
   Connection con = tl.get();
   // 判断它是不是事务专用,如果是,就不关闭! 如果不是事务专用,那么就要关闭!
   // 如果con == null,说明现在没有事务,那么connection一定不是事务专用的!
   //如果con != null,说明有事务,那么需要判断参数连接是否与con相等,若不等,说明参数连接不是事务专用连接
   if (con == null || con != connection)
    connection.close();
  } catch (Exception e) {
   logger.error("数据库连接释放失败!", e);
   throw new SQLException("数据库连接释放失败!");
  }
 }

}

最后,编写单元测试程序DBCPTest

代码语言:javascript
代码运行次数:0
运行
复制
public class DBCPTest {
 
 private static final int sumCount = 1000000;
 
 private static final int threadNum = 600;
 
 private void before(String path) {
  SysConstants.putValue(path);
  new DBCPService().insert("delete from t_test");
 }
 
 @Test
 public void testMysql() {
  long start = System.currentTimeMillis();
  String path = "config/mysql/dbcp-jdbc.properties";
  before(path);
  for (int i =0; i < 1; i++) {
   String sql = "insert into t_test(id,name) values('" +i+ "','dbcp-mysql-" + i + "')";
   new DBCPService().insert(sql);
  }
  System.out.println("耗时:" + (System.currentTimeMillis() - start));
 }
 
 @Test
 public void testThreadMysql() throws InterruptedException {
  String path = "config/mysql/dbcp-jdbc.properties";
  before(path);
  BlockingQueue<String> queue = new LinkedBlockingQueue<String>();
  for (int i = 0; i < sumCount; i++) {
   String sql = "insert into t_test(id,name) values('" +i+ "','dbcp-mysql-" + i + "')";
   queue.put(sql);
  }
  long start = System.currentTimeMillis();
  final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
  for (int i = 0; i < threadNum; i++) {
   final int finalI = i + 1;
   new Thread(new Runnable() {
    @Override
    public void run() {
     System.out.println("thread " + finalI + " start");
     boolean isGo = true;
     while (isGo) {
      String sql = queue.poll();
      if(sql != null) {
       new DBCPService().insert(sql);
      }else {
       isGo =false;
       System.out.println("thread " + finalI + " finish");
       countDownLatch.countDown();
      }
     }
    }
   }).start();
  }
  countDownLatch.await(); 
  System.out.println("耗时:" + (System.currentTimeMillis() - start));
 }

}

c3p0、druid的配置也类似,这里就不在重复介绍了!

三、性能测试

程序编写完成之后,下面我们就一起来结合各种不同的场景来测试一下各个数据连接池的表现。

为了进一步扩大测试范围,本次测试还将各个主流的数据库也拉入进去,测试的数据库分别是:mysql-5.7oracle-12postgresql-9.6

3.1、插入10万条数据

首先,我们来测试一下,各个数据库插入10万条数据,采用不同的数据源连接池,看看它们的表现如何?

  • 测试dbcp执行结果
  • 测试c3p0执行结果
  • 测试druid执行结果

从上面测试结果,我们可以基本得出如下结论:

  • 从数据连接池性能角度看:dbcp > druid > c3p0
  • 从数据库性能角度看:oracle > postgresql > mysql

其中druidpostgresql的支持性能最好,c3p0的表现比较差!

3.2、插入100万条数据

可能有的同学,还不太认可,下面我们就来测试一下插入100万条,看看它们的表现如何?

  • 测试dbcp执行结果
  • 测试c3p0执行结果
  • 测试druid执行结果

从上面测试结果,我们可以基本得出如下结论:

  • 从数据连接池性能角度看:druid性能比较稳定,dbcpc3p0都有某种程度的执行失败
  • 从数据库性能角度看:postgresql > oracle > mysql

还是一样的结论,druidpostgresql的支持性能最好,c3p0的表现比较差!

四、小结

从上面的测试结果,我们可以很清晰的看到,在数据连接池方面,druiddbcp旗鼓相当,而并发方面druid的稳定性大于dbcpc3p0相比druiddbcp,稳定性和执行速度要弱些。

在数据库方面,postgresql速度要优于oracle,而oracle对各个数据源的支持和稳定性要有优势,mysql相比oraclepostgresql,执行速度要弱些。

如果在实际开发中,数据源连接池推荐采用druid,数据库的选用方面 postgresql > oracle > mysql

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-05-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java极客技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Java Web学习总结之数据库连接池
  用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。如下图所示:
Java团长
2018/08/06
9670
JDBC和数据库连接池
JDBC是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。
宋先生
2019/07/18
6360
NIFI里的数据库连接池
通常我们在NIFI里最常见的使用场景就是读写关系型数据库,一些组件比如GenerateTableFetch、ExecuteSQL、PutSQL、ExecuteSQLRecord、PutDatabaseRecord等等,都会有一个属性配置大概叫Database Connection Pooling Service的,对应的接口是DBCPService,其实现类有:HiveConnectionPool DBCPConnectionPool DBCPConnectionPoolLookup。我们用的最多的就是DBCPConnectionPool。具体怎么配置这里就不赘述了,看对应的Controller Service文档就可以了。
@阿诚
2020/09/15
2.8K0
NIFI里的数据库连接池
【愚公系列】2023年03月 Java教学课程 113-JDBC的(数据库连接池)
数据库连接池是一种用于管理数据库连接的技术,它可以在应用程序启动时创建一定数量的数据库连接,然后将这些连接放入一个连接池中,当应用程序需要访问数据库时,就从连接池中获取一个连接,并在使用完毕后将连接归还给连接池。这样可以避免频繁地创建和销毁数据库连接,从而提高数据库访问效率和性能。数据库连接池通常包含以下属性:最小连接数、最大连接数、连接超时时间、空闲连接回收时间等。
愚公搬代码
2023/04/02
7420
【愚公系列】2023年03月 Java教学课程 113-JDBC的(数据库连接池)
delphi 数据库连接池-c3p0,DBCP,Druid(德鲁伊)数据库连接池
  普通的 JDBC 数据库连接使用 来获取到连接的,每次向数据库请求建立连接的时候,都要将 加载到内存中,再验证用户名和密码(需要花费0.05s ~ 1s的时间 ) 。需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接,这样的方式,将会消耗大量的资源和时间。数据库的连接资源并没有得到一个很好的重复利用 ,如果同时有 几百人甚至 几千人 在线,频繁的进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃。本博客后面会作相应的演示,请大家继续往后看下去。对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库。 何为Java的内存泄漏这种开发不能控制被创建的连接对象数,不能很好的管理连接的资源信息,系统资源会被毫无顾忌的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。 1.2 JDBC 连接数据库
宜轩
2022/12/29
8510
JavaJDBC:连接池
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
栗筝i
2022/12/01
7140
数据库连接池、dbutil_知识点全掌握
数据库连接池、dbutil 数据库连接池 1 数据库连接池的概念 用池来管理Connection,这可以重复使用Connection。有了池,所以我们就不用自己来创建Connection,而是通过池来
Java帮帮
2018/03/19
8280
数据库连接池、dbutil_知识点全掌握
数据库技术:数据库连接池,Commons DbUtils,批处理,元数据
实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程,为了解决此类性能问题,通常情况我们采用连接池技术,来共享连接 Connection。这样我们就不需要每次都创建连接、释放连接了,因为这些操作都交给了连接池。
RendaZhang
2020/09/08
1.1K0
数据库技术:数据库连接池,Commons DbUtils,批处理,元数据
JDBC和数据库连接池
说明:JDBC是Java提供一套用于数据库操作的接口APl, Java程序员只需要面向这套接口编程即可。不同的数据库厂商,需要针对这套接口,提供不同实现。
timerring
2023/05/24
9270
JDBC和数据库连接池
开源数据库连接池的使用
上篇博客刚刚说完如何去自定义一个数据库连接池,当然,这个自定义的数据库连接池是十分简易的,凭借自己的能力也无法写出优秀的连接池。但是,不用担心,我们可以使用开源的数据库连接池,开源的优势体现于此。
wangweijun
2020/01/19
1.4K0
5-数据库连接池
如果Connection对象是从连接池中直接获取的,则调用Connection的close方法不再关闭连接,而是将该连接归还给连接池
Ywrby
2022/10/27
3050
java JDBC数据库连接池技术(Druid ,c3p0)
这个原因与为什么使用线程池有点相似,都是为了提高资源的利用率,减少申请时间的浪费,提高程序的运行效率。 数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数 据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统 无尽的与数据库连接。
一只胡说八道的猴子
2020/09/27
7840
java JDBC数据库连接池技术(Druid ,c3p0)
C3P0连接池配置和实现详解「建议收藏」
一、C3P0配置 c3p0的配置方式分为三种,分别是 1.setters一个个地设置各个配置项 2.类路径下提供一个c3p0.properties文件 3.类路径下提供一个c3p0-config.xml文件
全栈程序员站长
2022/06/30
2.3K0
数据库连接池(Druid(德鲁伊))
普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection加载到内存中,再验证用户名和密码(得花费0.05s~1s的时间)。需要数据库连接的时候,就向数据库要求 一个,执行完成后再断开连接。这样的方式将会消耗大量的资源和时间。数据库的连接资源并没有得到很 好的重复利用。若同时有几百人甚至几千人在线,频繁的进行数据库连接操作将占用很多的系统资源,严 重的甚至会造成服务器的崩溃。
一个风轻云淡
2022/11/15
1.4K0
数据库连接池(Druid(德鲁伊))
常用数据库连接池 (DBCP、c3p0、Druid) 配置说明
数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池正是针对这个问题提出来的。
用户2409797
2018/08/30
1.7K0
[数据库连接池] Java数据库连接池--DBCP浅析.
前言 对于数据库连接池, 想必大家都已经不再陌生, 这里仅仅设计Java中的两个常用数据库连接池: DBCP和C3P0(后续会更新).  一. 为何要使用数据库连接池 假设网站一天有很大的访问量,数据库服务器就需要为每次连接创建一次数据库连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。 数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库连接池正式针对这个问题提出
一枝花算不算浪漫
2018/05/18
1.6K0
用了这么久的数据库连接池,你知道原理吗?
这次我们采取技术演进的方式来谈谈数据库连接池的技术出现过程及其原理,以及当下最流行的开源数据库连接池jar包。
Bug开发工程师
2019/06/17
1.2K0
用了这么久的数据库连接池,你知道原理吗?
jdbc是数据库连接池么_零之轨迹超详细攻略
1、数据库连接池的基本思想: 就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
全栈程序员站长
2022/09/30
5360
jdbc是数据库连接池么_零之轨迹超详细攻略
数据库连接(2) - 为什么C3P0连接池那么慢
在上一篇中我们介绍说客户端建立一次连接耗时太长(建立连接,设置字符集,autocommit等),如果在每个sql操作都需要经历建立连接,关闭连接。不仅应用程序响应慢,而且会产生很多临时对象,应用服务器GC压力大。另外数据库server端对连接也有限制,比如MySQL默认151个连接(实际环境中一般会调大这个值,尤其是多个服务时)
方丈的寺院
2019/08/05
1.1K0
数据库连接(2) - 为什么C3P0连接池那么慢
数据库连接池入门(c3p0、Druid)
数据库连接池其实就是一个容器(集合),存放数据库连接的容器。 当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
共饮一杯无
2022/11/28
4680
数据库连接池入门(c3p0、Druid)
相关推荐
Java Web学习总结之数据库连接池
更多 >
目录
  • 二、程序实例
    • 2.1、创建测试表
    • 2.2、 编写测试用例
  • 三、性能测试
    • 3.1、插入10万条数据
    • 3.2、插入100万条数据
  • 四、小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档