常用开源数据库连接池C3P0、Druid介绍

概述

很多时候,连接的混乱管理所造成的系统资源开销过大成为制约大型企业级应用效率的瓶颈。因为每一次WEB请求都要建立一次数据库连接,建立连接是一个耗费资源的活动,每次都得花费0.05-1s的时间,而且系统还要分配内存资源。

数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。于现在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从缓冲池中取出一个,使用完毕以后再放回去即可。

C3P0

C3P0是一款优秀的开源数据库连接池,被广泛使用。

使用

maven依赖:

<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>

基本配置:

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!-- 默认配置,如果没有指定则使用这个配置 -->
    <default-config>
        <property name="user">root</property>
        <property name="password">123456</property>
        <property name="jdbcUrl">jdbc:mysql://192.168.91.1:3306/test</property>
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
    </default-config>
    <!-- 命名的配置,可以通过方法调用实现 -->
    <named-config name="test">
        <!-- ... -->
    </named-config>
</c3p0-config>

其他配置:

参数 默认值 解释
initialPoolSize 3 连接池初始化时创建的连接数(介于maxPoolSize和minPoolSize之间)
maxPoolSize 15 连接池中拥有的最大连接数,如果获得新连接时会使连接总数超过这个值则不会再获取新连接,而是等待其他连接释放,所以这个值有可能会设计地很大
minPoolSize 3 连接池保持的最小连接数,后面的maxIdleTimeExcessConnections跟这个配合使用来减轻连接池的负载
acquireIncrement 3 连接池在无空闲连接可用时一次性创建的新数据库连接数
maxIdleTime 0 连接的最大空闲时间,如果超过这个时间,某个数据库连接还没有被使用,则会断开掉这个连接如果为0,则永远不会断开连接
maxConnectorAge 0 连接的最大绝对年龄,单位是秒,0表示绝对年龄无限大
maxIdleTimeExcessConnection 0 单位秒,为了减轻连接池的负载,当连接池经过数据访问高峰创建了很多连接,但是后面连接池不需要维护这么多连接,必须小于maxIdleTime.配置不为0,则将连接池的数量保持到minPoolSize
automaticTestTable null 如果不为null,c3p0将生成指定名称的空表,使用该表来测试连接
connectionTesterClassName com.mchange.v2.c3p0.impl.
DefaultConnectionTester
通过实现ConnectionTester或QueryConnectionTester的类来测试连接。类名需制定全路径。
idleConnectionTestPeriod 0 每个几秒检查所有连接池中的空闲连接
preferredTestQuery null 定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个一显著提高测试速度。注意: 测试的表必须在初始数据源的时候就存在
testConnectionOnCheckin FALSE 如果设为true那么在取得连接的同时将校验连接的有效性
testConnectionOnCheckout FALSE 如果为true,在连接释放的同事将校验连接的有效性。
maxStatements 0 JDBC的标准参数,用以控制数据源内加载d的PreparedStatements数量
maxStatementsPerConnection 0 maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数
statementCacheNum-
DeferredCloseThreads
0 如果大于零,则语句池将延迟物理close()缓存语句直到其父连接未被任何客户端使用,或者在其内部(例如在测试中)由池本身使用。
acquireRetryAttempts 30 定义在从数据库获取新连接失败后重复尝试的次数
acquireRetryDelay 1000 两次连接间隔时间,单位毫秒
breakAfterAcquireFailure FALSE 获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效 保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试 获取连接失败后该数据源将申明已断开并永久关闭
autoCommitOnClose FALSE 连接关闭时默认将所有未提交的操作回滚。如果为true,则未提交设置为待提交而不是回滚。
forceIgnoreUnresolvedTransactions FALSE 官方文档建议这个不要设置为true
checkoutTimeout 0 当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒。
factoryClassLocation 0 指定c3p0 libraries的路径,如果(通常都是这样)在本地即可获得那么无需设置,默认null即可
numHelperThreads 3 c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能通过多线程实现多个操作同时被执行

使用:

DataSource dataSource = new ComboPooledDataSource();

它会自动去类路径下查找名为c3p0-config.xml配置文件并加载。

可以使用命名的配置:

DataSource dataSource = new ComboPooledDataSource("test");

使用的就是name="test"named-config

Druid

Druid是阿里开源的一个数据库连接池,借鉴了C3P0等成熟连接池的思想,具有诸多优点

使用

maven依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.2</version>
</dependency>

基本配置:

driverClassName = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://192.168.91.1:3306/test
username = root
password = 123456

其他配置:

配置 默认值 说明
initialSize 0 初始化时建立物理连接的个数。初始化发生在显式调用 init 方法,或者第一次getConnection 时
maxActive 8 最大允许的连接数
maxIdle 已过期
minIdle 最小的空闲连接数
maxWait 获取连接时最大等待时间,单位毫秒。配置了 maxWait 之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置 useUnfairLock 属性为 true 使用非公平锁。
validationQuery 用来检测连接是否有效的 sql,要求是一个查询语句。
testOnBorrow TRUE 申请连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。
testOnReturn FALSE 归还连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能
testWhileIdle FALSE 设置空闲时是否检测连接可用性。建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,执行 validationQuery 检测连接是否有效。
timeBetweenEvictionRunsMillis 检测需要关闭的空闲连接的间隔时间
minEvictableIdleTimeMillis 连接在池中的最小生存时间
connectionInitSqls 物理连接初始化的时候执行的sql
filters 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters 类型是List\<com.alibaba.druid.filter.Filter>,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系

使用:

Properties properties = new Properties();
InputStream in = Demo1.class.getClassLoader().getResourceAsStream("druid.properties");
properties.load(in);
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//使用连接池
conn = dataSource.getConnection();

源码

public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
    init();

    if (filters.size() > 0) {
        FilterChainImpl filterChain = new FilterChainImpl(this);
        return filterChain.dataSource_connect(this, maxWaitMillis);
    } else {
        return getConnectionDirect(maxWaitMillis);
    }
}

DruidPooledConnection内部包装了一个Connection,增强了close等方法

@Override
public void close() throws SQLException {
    if (this.disable) {
        return;
    }

    DruidConnectionHolder holder = this.holder;
    if (holder == null) {
        if (dupCloseLogEnable) {
            LOG.error("dup close");
        }
        return;
    }

    DruidAbstractDataSource dataSource = holder.getDataSource();
    boolean isSameThread = this.getOwnerThread() == Thread.currentThread();

    if (!isSameThread) {
        dataSource.setAsyncCloseConnectionEnable(true);
    }

    if (dataSource.isAsyncCloseConnectionEnable()) {
        syncClose();
        return;
    }

    for (ConnectionEventListener listener : holder.getConnectionEventListeners()) {
        listener.connectionClosed(new ConnectionEvent(this));
    }


    List<Filter> filters = dataSource.getProxyFilters();
    if (filters.size() > 0) {
        FilterChainImpl filterChain = new FilterChainImpl(dataSource);
        filterChain.dataSource_recycle(this);
    } else {
        recycle();
    }

    this.disable = true;
}

【附】了解:DBUtils的使用

QueryRunner、DbUtils、ResultSetHandler。

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/%e5%b8%b8%e7%94%a8%e5%bc%80%e6%ba%90%e6%95%b0%e6%8d%ae%e5%ba%93%e8%bf%9e%e6%8e%a5%e6%b1%a0c3p0%e3%80%81druid%e4%bb%8b%e7%bb%8d/

发表评论

电子邮件地址不会被公开。