跳转至

Java 数据库连接池

当你的才华还撑不起你的野心的时候,你就应该静下心来学习。

1. 介绍

  数据库连接池的目的是为了减少频繁开关连接的时间,提高整个系统的响应能力通过分析发现应该具备几个属性值

  • 初始大小
  • 每次扩容的大小
  • 连接池的最大个数
  • 空闲连接的死亡时间

  池(Pool)技术在一定程度上可以明显优化服务器应用程序的性能,提高程序执行效率和降低系统资源开销。这里所说的池是一种广义上的池,比如数据库连接池、线程池、内存池、对象池等。其中,对象池可以看成保存对象的容器,在进程初始化时创建一定数量的对象。需要时直接从池中取出一个空闲对象,用完后并不直接释放掉对象,而是再放到对象池中以方便下一次对象请求可以直接复用。其他几种池的设计思想也是如此,池技术的优势是,可以消除对象创建所带来的延迟,从而提高系统的性能。

  要了解Java连接池我们先要了解数据库连接池(connection pool)的原理,Java连接池正是数据库连接池在Java上的应用。——我们知道,对于共享资源,有一个很著名的设计模式:资源池(Resource Pool)。

  该模式正是为了解决资源的频繁分配﹑释放所造成的问题。为解决上述问题,可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量﹑使用情况,为系统开发﹑测试及性能调整提供依据。

  C3P0是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。(主页:http://sourceforge.net/projects/c3p0/

  BoneCP是一个开源的快速的JDBC连接池。BoneCP很小,只有四十几K(运行时需要log4j和GoogleCollections的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。另外个人觉得 BoneCP 有个缺点是,JDBC驱动的加载是在连接池之外的,这样在一些应用服务器的配置上就不够灵活。当然,体积小并不是 BoneCP 优秀的原因,BoneCP 到底有什么突出的地方呢,请看看性能测试报告。(主页:http://jolbox.com/

  DBCP(DatabaseConnectionPool)是一个依赖Jakartacommons-pool对象池机制的数据库连接池,Tomcat的数据源使用的就是DBCP。目前DBCP有两个版本分别是 1.3 和 1.4。1.3 版本对应的是 JDK 1.4-1.5 和 JDBC 3,而1.4 版本对应 JDK 1.6 和 JDBC 4。因此在选择版本的时候要看看你用的是什么 JDK版本了,功能上倒是没有什么区别。(主页:http://commons.apache.org/dbcp/

1.1 什么是连接池

  用池来管理Connection,这样可以重复使用Connection。有了池,所以我们就不用自己来创建Connection,而是通过池来获取Connection对象。当使用完Connection后,调用Connection的close()方法也不会真的关闭Connection,而是把Connection“归还”给池。池就可以再利用这个Connection对象了。

跨平台

Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!常见的连接池:

  • DBCP
  • C3P0
  • Druid
  • hikariCP

2. DBCP

2.1 介绍

   几乎很少使用了 。需要的jar包有 commons-dbutils , commons-dbcp , commons-pool , mysql-connector-java,DBCP也是一个开源的连接池,是Apache Common成员之一,在企业开发中也比较常见,tomcat内置的连接池。如果我们想要使用它的功能的话,需要导入jar包,可以用Maven方式进行下载包,它可以帮我们解决一些依赖关系。

2.2 常见配置项

分类 属性 描述
必须项 driverClassName 数据库驱动名称
url 数据库的地址
username 用户名
password 密码
基本项(扩展) maxActive 最大连接数量
minIdle 最小空闲连接
maxIdle 最大空闲连接
initialSize 初始化连接

想要查看更详细的信息可参考DBCP的官网

http://commons.apache.org/proper/commons-dbcp/configuration.html
http://commons.apache.org/proper/commons-dbcp/
http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi
http://commons.apache.org/proper/commons-pool/download_pool.cgi

2.3 依赖

提前导入以下包。

 D:\数据库连接池\dbcp 的目录

2020/01/14  21:54    <DIR>          .
2020/01/14  21:54    <DIR>          ..
2020/01/14  21:54           173,567 commons-dbcp2-2.1.jar
2020/01/14  21:54    <DIR>          commons-dbcp2-2.7.0
2020/01/14  21:54         2,104,906 commons-dbcp2-2.7.0-bin.zip
2020/01/14  21:54            61,829 commons-logging-1.2.jar
2020/01/14  21:54           109,568 commons-pool2-2.3.jar
               4 个文件      2,449,870 字节
               3 个目录 11,040,313,344 可用字节

跨平台
跨平台
跨平台

2.4 code

DBCPTest.class
package com.cmz.pool.dbcp;

import org.apache.commons.dbcp2.BasicDataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class DBCPTest {
    public static void main(String[] args) {
        //数据库的连接池资源,在之后操作的时候只需要从池中获取即可
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("root");

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            connection = dataSource.getConnection();
            String sql = "select * from emp";
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();

            while(resultSet.next()){
                System.out.println(resultSet.getString("ename"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果
SMITH
ALLEN
WARD
JONES
MARTIN
BLAKE
CLARK
SCOTT
KING
TURNER
ADAMS
JAMES
FORD
MILLER

3. C3P0

3.1 介绍

  C3P0是一个开源的JDBC连接池,目前使用它的开源项目有Hibernate,Spring等。

  数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。获取一个连接,系统要在背后做很多消耗资源的事情,大多时候,创建连接的时间比执行sql语句的时间还要长。用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。

3.2 常见配置项

<!--acquireIncrement:链接用完了自动增量3个。 -->
    <property name="acquireIncrement">3</property>

    <!--acquireRetryAttempts:链接失败后重新试30次。-->
    <property name="acquireRetryAttempts">30</property>

    <!--acquireRetryDelay;两次连接中间隔1000毫秒。 -->
    <property name="acquireRetryDelay">1000</property>

    <!--autoCommitOnClose:连接关闭时默认将所有未提交的操作回滚。 -->
    <property name="autoCommitOnClose">false</property>

    <!--automaticTestTable:c3p0测试表,没什么用。-->
    <property name="automaticTestTable">Test</property>

    <!--breakAfterAcquireFailure:出错时不把正在提交的数据抛弃。-->
    <property name="breakAfterAcquireFailure">false</property>

    <!--checkoutTimeout:100毫秒后如果sql数据没有执行完将会报错,如果设置成0,那么将会无限的等待。 --> 
    <property name="checkoutTimeout">100</property>

    <!--connectionTesterClassName:通过实现ConnectionTester或QueryConnectionTester的类来测试连接。类名需制定全路径。Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester-->
    <property name="connectionTesterClassName"></property>

    <!--factoryClassLocation:指定c3p0 libraries的路径,如果(通常都是这样)在本地即可获得那么无需设置,默认null即可。-->
    <property name="factoryClassLocation">null</property>

    <!--forceIgnoreUnresolvedTransactions:作者强烈建议不使用的一个属性。--> 
    <property name="forceIgnoreUnresolvedTransactions">false</property>

    <!--idleConnectionTestPeriod:每60秒检查所有连接池中的空闲连接。--> 
    <property name="idleConnectionTestPeriod">60</property>

    <!--initialPoolSize:初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。 --> 
    <property name="initialPoolSize">3</property>

    <!--maxIdleTime:最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。-->
    <property name="maxIdleTime">60</property>

    <!--maxPoolSize:连接池中保留的最大连接数。 -->
    <property name="maxPoolSize">15</property>

    <!--maxStatements:最大链接数。-->
    <property name="maxStatements">100</property>

    <!--maxStatementsPerConnection:定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0  -->
    <property name="maxStatementsPerConnection"></property>

    <!--numHelperThreads:异步操作,提升性能通过多线程实现多个操作同时被执行。Default: 3--> 
    <property name="numHelperThreads">3</property>

    <!--overrideDefaultUser:当用户调用getConnection()时使root用户成为去获取连接的用户。主要用于连接池连接非c3p0的数据源时。Default: null--> 
    <property name="overrideDefaultUser">root</property>

    <!--overrideDefaultPassword:与overrideDefaultUser参数对应使用的一个参数。Default: null-->
    <property name="overrideDefaultPassword">password</property>

    <!--password:密码。Default: null--> 
    <property name="password"></property>

    <!--preferredTestQuery:定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个一显著提高测试速度。注意: 测试的表必须在初始数据源的时候就存在。Default: null-->
    <property name="preferredTestQuery">select id from test where id=1</property>

    <!--propertyCycle:用户修改系统配置参数执行前最多等待300秒。Default: 300 --> 
    <property name="propertyCycle">300</property>

    <!--testConnectionOnCheckout:因性能消耗大请只在需要的时候使用它。Default: false -->
    <property name="testConnectionOnCheckout">false</property>

    <!--testConnectionOnCheckin:如果设为true那么在取得连接的同时将校验连接的有效性。Default: false -->
    <property name="testConnectionOnCheckin">true</property>

    <!--user:用户名。Default: null-->
    <property name="user">root</property>

    <!--usesTraditionalReflectiveProxies:动态反射代理。Default: false-->
    <property name="usesTraditionalReflectiveProxies">false</property>

想要查看更详细的信息可参考DBCP的官网

https://www.mchange.com/projects/c3p0/

3.3 依赖

 D:\数据库连接池\c3p0 的目录

2020/01/14  23:54    <DIR>          .
2020/01/14  23:54    <DIR>          ..
2020/01/14  23:54           499,844 c3p0-0.9.5.4.jar
2020/01/14  23:54           624,036 mchange-commons-java-0.2.15.jar
               2 个文件      1,123,880 字节
               2 个目录 11,023,990,784 可用字节

  命名必须为c3p0-config.xml,必须放在src目录下,c3p0包会默认加载src目录下的c3p0-config.xml文件。或者也可以使用c3p0.properties,但是和c3p0-config.xml一样必须在src目录下,因为源码已经写死了位置。

3.4 code

c3p0.properties
#JDBC具备自己的规范,在JDBC4之前是必须要填写驱动名称的,但是之后版本不需要填写,test是库
c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql://localhost:3306/test  
c3p0.user=root
c3p0.password=root
c3p0-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
        <property name="user">root</property>
        <property name="password">123456</property>
    </default-config>
</c3p0-config>
C3P0Test.class
package com.cmz.pool.c3p0;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class C3P0Test {

    public static Connection connection;
    public static ComboPooledDataSource dataSource;

    public static void getConnection(){
        dataSource = new ComboPooledDataSource();

    }

    public static void queryData(){
        try {
            connection = dataSource.getConnection();
            String sql = "select * from emp";
            PreparedStatement ps = connection.prepareStatement(sql);
            ResultSet rs = ps.executeQuery();
            while (rs.next()){
                System.out.println(rs.getString("ename"));

            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }


    public static void main(String[] args) throws Exception {
        /**
         * 直接在类的方法中设置连接的参数,一般没人使用,不太建议,最好使用配置文件
         */
        /*
        ComboPooledDataSource cpds = new ComboPooledDataSource();
        cpds.setDriverClass("com.mysql.jdbc.Driver"); //loads the jdbc driver
        cpds.setJdbcUrl( "jdbc:mysql://localhost:3306/test" );
        cpds.setUser("root");
        cpds.setPassword("root");
        Connection connection = cpds.getConnection();
        System.out.println(connection);
        connection.close();
        */

        getConnection();
        queryData();
    }
}
运行结果
二月 16, 2020 10:01:03 下午 com.mchange.v2.log.MLog 
信息: MLog clients using java 1.4+ standard logging.
二月 16, 2020 10:01:06 下午 com.mchange.v2.c3p0.C3P0Registry 
信息: Initializing c3p0-0.9.5.4 [built 23-March-2019 23:00:48 -0700; debug? true; trace: 10]
二月 16, 2020 10:01:06 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource 
信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1hgekn7a8lq549wfbleu2|7591083d, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1hgekn7a8lq549wfbleu2|7591083d, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/test, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
SMITH
ALLEN
WARD
JONES
MARTIN
BLAKE
CLARK
SCOTT
KING
TURNER
ADAMS
JAMES
FORD
MILLER

Process finished with exit code 0

4. druid

https://github.com/alibaba/druid
https://github.com/alibaba/druid/wiki
properties
#驱动名称
driverClassName=com.mysql.jdbc.Driver
#url
url=jdbc:mysql://localhost:3306/test
#用户名
username=root
#密码
password=root
#配置初始化大小、最小、最大
initialSize=5
minIdle=10
maxActive=8
#配置监控系统拦截的filters:监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
filters=stat
#配置获取连接等待超时的时间
maxWait=60000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis=60000
#配置一个连接在池中最小的生存的时间,单位是毫秒
minEvictableIdleTimeMillis=600000
maxEvictableIdleTimeMillis=900000
#建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
testWhileIdle=true
#申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnBorrow=false
#归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testOnReturn=false
#是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
poolPreparedStatements=true
#要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
maxOpenPreparedStatements=20
#asyncInit是1.1.4中新增加的配置,如果有initialSize数量较多时,打开会加快应用启动时间
asyncInit=true

DruidTest

package com.cmz.pool.druid;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.util.Properties;

public class DruidTest {
    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();
        FileInputStream fileInputStream = new FileInputStream("src/com/cmz/pool/druid/druid.properties");
        properties.load(fileInputStream);

        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        connection.close();
    }
}

输出

二月 17, 2020 11:58:06 上午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@7c30a502

4. HiKariCP

导入包

HikariCP-3.4.1-javadoc.jar
HikariCP-3.4.1-sources.jar
HikariCP-3.4.1.jar
slf4j-api-2.0.0-alpha1.jar

https://gitee.com/mirrors/hikaricp?_from=gitee_search
https://github.com/brettwooldridge/HikariCP

HikariCPTest

package com.cmz.pool.HikariCP;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import java.sql.Connection;
import java.sql.SQLException;

public class HikariCPTest {
    public static void main(String[] args) throws SQLException {
//        HikariConfig config = new HikariConfig();
//        config.setJdbcUrl("jdbc:mysql://localhost:3306/demo");
//        config.setUsername("root");
//        config.setPassword("123456");
//
//        HikariDataSource ds = new HikariDataSource(config);
//        Connection connection = ds.getConnection();
//        System.out.println(connection);
//        connection.close();

//        HikariDataSource hikariDataSource = new HikariDataSource();
//        hikariDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/demo");
//        hikariDataSource.setUsername("root");
//        hikariDataSource.setPassword("123456");

        HikariConfig config = new HikariConfig("src/com/cmz/pool/hikariCP/hikariCP.properties");
        HikariDataSource dataSource = new HikariDataSource(config);

        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        connection.close();
    }
}

输出

SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#noProviders for further details.
HikariProxyConnection@873415566 wrapping com.mysql.jdbc.JDBC4Connection@30c7da1e