主流Java数据库连接池比较与开发配置实战
1. 数据库连接池概述
数据库连接的建立是一种耗时、性能低、代价高的操作,频繁的数据库连接的建立和关闭极大的影响了系统的性能。数据库连接池是系统初始化过程中创建一定数量的数据库连接放于连接池中,当程序需要访问数据库时,不再建立一个新的连接,而是从连接池中取出一个已建立的空闲连接,使用完毕后,程序将连接归还到连接池中,供其他请求使用,从而实现的资源的共享,连接的建立、断开都由连接池自身来管理。
数据库连接池为系统的运行带来了以下优势:昂贵的数据库连接资源得到重用;减少了数据库连接建立和释放的时间开销,提高了系统响应速度;统一的数据库连接管理,避免了连接资源的泄露。
数据库连接池运行机制:
系统初始化时创建连接池,程序操作数据库时从连接池中获取空闲连接,程序使用完毕将连接归还到连接池中,系统退出时,断开所有数据库连接并释放内存资源。
2. 主流数据库连接池比较
常用的主流开源数据库连接池有 C3P0、DBCP、Tomcat Jdbc Pool、BoneCP、Druid 等
C3p0: 开源的 JDBC 连接池,实现了数据源和 JNDI 绑定,支持 JDBC3 规范和 JDBC2 的标准扩展。目前使用它的开源项目有 Hibernate、Spring 等。单线程,性能较差,适用于小型系统,代码 600KB 左右。
DBCP (Database Connection Pool): 由 Apache 开发的一个 Java 数据库连接池项目, Jakarta commons-pool 对象池机制,Tomcat 使用的连接池组件就是 DBCP。单独使用 dbcp 需要 3 个包:common-dbcp.jar,common-pool.jar,common-collections.jar,预先将数据库连接放在内存中,应用程序需要建立数据库连接时直接到连接池中申请一个就行,用完再放回。单线程,并发量低,性能不好,适用于小型系统。
Tomcat Jdbc Pool:Tomcat 在 7.0 以前都是使用 common-dbcp 做为连接池组件,但是 dbcp 是单线程,为保证线程安全会锁整个连接池,性能较差,dbcp 有超过 60 个类,也相对复杂。Tomcat 从 7.0 开始引入了新增连接池模块叫做 Tomcat jdbc pool,基于 Tomcat JULI,使用 Tomcat 日志框架,完全兼容 dbcp,通过异步方式获取连接,支持高并发应用环境,超级简单核心文件只有 8 个,支持 JMX,支持 XA Connection。
BoneCP:官方说法 BoneCP 是一个高效、免费、开源的 Java 数据库连接池实现库。设计初衷就是为了提高数据库连接池性能,根据某些测试数据显示,BoneCP 的速度是最快的,要比当时第二快速的连接池快 25 倍左右,完美集成到一些持久化产品如 Hibernate 和 DataNucleus 中。BoneCP 特色:高度可扩展,快速;连接状态切换的回调机制;允许直接访问连接;自动化重置能力;JMX 支持;懒加载能力;支持 XML 和属性文件配置方式;较好的 Java 代码组织,100% 单元测试分支代码覆盖率;代码 40KB 左右。
Druid:Druid 是 Java 语言中最好的数据库连接池,Druid 能够提供强大的监控和扩展功能,是一个可用于大数据实时查询和分析的高容错、高性能的开源分布式系统,尤其是当发生代码部署、机器故障以及其他产品系统遇到宕机等情况时,Druid 仍能够保持 100% 正常运行。主要特色:为分析监控设计;快速的交互式查询;高可用;可扩展;Druid 是一个开源项目,源码托管在 github 上。
主流连接池各项功能对比如下:
3. 数据库连接池 Spring 集成配置与 JNDI 配置
下面针对每一种连接池的使用方法,在开发中如何配置给出 spring 集成配置和在 tomcat 的 conf/context.xml 文件中配置 2 种方式,限于篇幅只给出基本参数,详细参数可自行研究。
3.1 阿里 Druid 连接池
Maven 依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.28</version>
</dependency>
Spring 集成配置方式
- <!--Spring Druid 数据源配置 -->
- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
- <!-- 基本属性 url、user、password -->
- <property name="url" value="${jdbc.url}" />
- <property name="username" value="${jdbc.username}" />
- <property name="password" value="${jdbc.password}" />
- <!-- 配置初始化大小、最小、最大 -->
- <property name="initialSize" value="1" />
- <property name="minIdle" value="1" />
- <property name="maxActive" value="20" />
- <!-- 配置获取连接等待超时的时间 -->
- <property name="maxWait" value="60000" />
- <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
- <property name="timeBetweenEvictionRunsMillis" value="60000" />
- <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
- <property name="minEvictableIdleTimeMillis" value="300000" />
- <!-- 打开 PSCache,并且指定每个连接上 PSCache 的大小 -->
- <property name="poolPreparedStatements" value="true" />
- <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
- <!-- 配置监控统计拦截的 filters,去掉后监控界面 sql 无法统计 -->
- <property name="filters" value="stat" />
- </bean>
- Web.xml 配置
- <!--druid WebStatFilter 用于采集 web-jdbc 关联监控的数据 -->
- <filter>
- <filter-name>DruidWebStatFilter</filter-name>
- <filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
- <init-param>
- <param-name>exclusions</param-name>
- <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>DruidWebStatFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <!--druid 访问监控界面 /druid/index.html-->
- <servlet>
- <servlet-name>DruidStatView</servlet-name>
- <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
- </servlet>
- <servlet-mapping>
- <servlet-name>DruidStatView</servlet-name>
- <url-pattern>/druid/*</url-pattern>
- </servlet-mapping>
Tomcat 中 context.xml 文件 JNDI 配置方式
com.alibaba.druid.pool.DruidDataSourceFactory 实现了 javax.naming.spi.ObjectFactory,可以作为 JNDI 数据源来配置
<TOMCAT_HOME>/conf/context.xml 配置 JNDI 方式
- <Resource
- name="jdbc/MysqlDataSource"
- factory="com.alibaba.druid.pool.DruidDataSourceFactory"
- auth="Container"
- type="javax.sql.DataSource"
- driverClassName="com.mysql.jdbc.Driver"
- url="jdbc:mysql://192.168.1.233:3306/lead_oams?useUnicode=true&characterEncoding=utf-8"
- username="lead_system"
- password="password"
- maxActive="50"
- maxWait="10000"
- removeabandoned="true"
- removeabandonedtimeout="60"
- logabandoned="false"
- filters="stat"/>
web.xml 配置
- <!--MySQL 数据库 JNDI 数据 -->
- <resource-ref>
- <description>MySQL DB Connection</description>
- <res-ref-name>jdbc/MysqlDataSource</res-ref-name>
- <res-type>javax.sql.DataSource</res-type>
- <res-auth>Container</res-auth>
- </resource-ref>
Java 代码中获取 JNDI 数据源
//1、初始化名称查找上下文
Context ctx =new InitialContext();
//2、通过 JNDI 名称找到 DataSource
DruidDataSource ds = (DruidDataSource)ctx.lookup("java:comp/env/jdbc/MysqlDataSource");
//3、通过 ds 获取数据库连接对象
Connectionconn = ds.getConnection();
3.2 BoneCP 连接池
Maven 依赖
<dependency> <groupId>com.jolbox</groupId> <artifactId>bonecp-spring</artifactId> <version>0.8.0.RELEASE</version> </dependency>
Spring 集成 BoneCP 配置方式
- <!-- Spring BoneCP 数据源配置 -->
- <bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
- <!-- 数据库驱动 -->
- <property name="driverClass" value="${jdbc.driver}" />
- <!-- 相应驱动的 jdbcUrl -->
- <property name="jdbcUrl" value="${jdbc.url}" />
- <!-- 数据库的用户名 -->
- <property name="username" value="${jdbc.username}" />
- <!-- 数据库的密码 -->
- <property name="password" value="${jdbc.password}" />
- <!-- 检查数据库连接池中空闲连接的间隔时间,单位是分,默认值:240,如果要取消则设置为 0 -->
- <property name="idleConnectionTestPeriod" value="60" />
- <!-- 连接池中未使用的链接最大存活时间,单位是分,默认值:60,如果要永远存活设置为 0 -->
- <property name="idleMaxAge" value="30" />
- <!-- 每个分区最大的连接数 -->
- <property name="maxConnectionsPerPartition" value="150" />
- <!-- 每个分区最小的连接数 -->
- <property name="minConnectionsPerPartition" value="5" />
- </bean>
Tomcat 中 BoneCP 使用 JNDI 配置方式
- <Resource
- name="JNDIName"
- auth="Container"
- type="com.jolbox.bonecp.BoneCPDataSource"
- factory="org.apache.naming.factory.BeanFactory"
- driverClass="oracle.jdbc.driver.OracleDriver"
- username="root"
- password="root"
- jdbcUrl="jdbc:mysql://localhost:3306/test"
- idleConnectionTestPeriod="0"
- idleMaxAge="10"
- partitionCount="1"
- maxConnectionsPerPartition="5"
- minConnectionsPerPartition="1"
- connectionTestStatement=""
- initSQL="select 1 from dual"/>
Java 代码中获取 JNDI 数据源
//1、初始化名称查找上下文
Context ctx =new InitialContext();
//2、通过 JNDI 名称找到 DataSource
DataSource ds= (DataSource) ctx.lookup("java:comp/env/jdbc/MysqlDataSource");
//3、通过 ds 获取数据库连接对象
Connectionconn = ds.getConnection();
3.3 Tomcat Jdbc Pool 连接池
Maven 依赖
<dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-jdbc</artifactId> <version>7.0.75</version> </dependency> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-juli</artifactId> <version>7.0.75</version> </dependency>
Spring 集成 Tomcat Jbdc Pool 配置方式
- <!--tomcat jdbc pool 数据源配置 -->
- <bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
- <property name="poolProperties">
- <bean class="org.apache.tomcat.jdbc.pool.PoolProperties">
- <!--driverClassName url username password-->
- <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
- <property name="url" value="jdbc:mysql://localhost:3306/test?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8"/>
- <property name="username" value="root"/>
- <property name="password" value="root"/>
- <!--jmx support-->
- <property name="jmxEnabled" value="true"/>
- <property name="testWhileIdle" value="true"/>
- <property name="testOnBorrow" value="true"/>
- <property name="testOnReturn" value="false"/>
- <property name="validationInterval" value="30000"/>
- <property name="validationQuery" value="SELECT 1"/>
- <property name="timeBetweenEvictionRunsMillis" value="30000"/>
- <!-- 最大连接 -->
- <property name="maxActive" value="50"/>
- <!-- 初始化连接 -->
- <property name="initialSize" value="5"/>
- <!-- 最长等待时间 ms-->
- <property name="maxWait" value="10000"/>
- <property name="minEvictableIdleTimeMillis" value="30000"/>
- <property name="minIdle" value="10"/>
- <!-- 是否允许日志 -->
- <property name="logAbandoned" value="false"/>
- <property name="removeAbandoned" value="true"/>
- <property name="removeAbandonedTimeout" value="60"/>
- <property name="jdbcInterceptors" value="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"/>
- </bean>
Tomcat 中 context.xml 文件 JNDI 配置方式
- <Resource
- name="jdbc/test"
- auth="Container"
- factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
- testWhileIdle="true"
- testOnBorrow="true"
- testOnReturn="false"
- validationQuery="SELECT 1"
- validationInterval="30000"
- timeBetweenEvictionRunsMillis="30000"
- driverClassName="com.mysql.jdbc.Driver"
- maxActive="100"
- maxIdle="40"
- maxWait="12000"
- initialSize="10"
- removeAbandonedTimeout="60"
- removeAbandoned="true"
- logAbandoned="true"
- minEvictableIdleTimeMillis="30000"
- jmxEnabled="true"
- jdbcInterceptors=
- "org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
- username="root"
- password="root"
- type="javax.sql.DataSource"
- url="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8"/>
Java 代码中获取 JNDI 数据源
//1、初始化名称查找上下文
Context ctx =new InitialContext();
//2、通过 JNDI 名称找到 DataSource
DataSource ds= (DataSource) ctx.lookup("java:comp/env/jdbc/test");
//3、通过 ds 获取数据库连接对象
Connectionconn = ds.getConnection();
3.4 Apache DBCP 连接池
Maven 依赖
<dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.2</version> </dependency> <dependency> <groupId>commons-pool</groupId> <artifactId>commons-pool</artifactId> <version>1.6</version> </dependency>
Spring 集成 DBCP 配置方式
- <!-- 配置 dbcp 数据源 -->
- <bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
- <property name="driverClassName" value="${jdbc.driverClassName}"/>
- <property name="url" value="${jdbc.url}"/>
- <property name="username" value="${jdbc.username}"/>
- <property name="password" value="${jdbc.password}"/>
- <!-- 池启动时创建的连接数量 -->
- <property name="initialSize" value="5"/>
- <!-- 同一时间可以从池分配的最多连接数量。设置为 0 时表示无限制。 -->
- <property name="maxActive" value="50"/>
- <!-- 池里不会被释放的最多空闲连接数量。设置为 0 时表示无限制。 -->
- <property name="maxIdle" value="10"/>
- <!-- 在不新建连接的条件下,池中保持空闲的最少连接数。 -->
- <property name="minIdle" value="3"/>
- <!-- 设置自动回收超时连接 -->
- <property name="removeAbandoned" value="true" />
- <!-- 自动回收超时时间 (以秒数为单位) -->
- <property name="removeAbandonedTimeout" value="200"/>
- <!-- 设置在自动回收超时连接的时候打印连接的超时错误 -->
- <property name="logAbandoned" value="true"/>
- <!-- 等待超时以毫秒为单位,在抛出异常之前,池等待连接被回收的最长时间(当没有可用连接时)。设置为 -1 表示无限等待。-->
- <property name="maxWait" value="100"/>
- </bean>
Tomcat 中 context.xml 文件 JNDI 配置方式
- <Resource name="/jdbc/test"
- type="javax.sql.DataSource"
- driverClassName="com.sybase.jdbc3.jdbc.SybDataSource"
- url="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8"
- username="root"
- password="root"
- initialSize="5"
- maxActive="50"
- maxIdle="10"
- minIdle="3"
- maxWait="50000" />
Java 代码中获取 JNDI 数据源
//1、初始化名称查找上下文
Context ctx =new InitialContext();
//2、通过 JNDI 名称找到 DataSource
DataSource ds= (DataSource) ctx.lookup("java:comp/env/jdbc/test");
//3、通过 ds 获取数据库连接对象
Connectionconn = ds.getConnection();
3.5 C3p0 连接池
Maven 依赖
<dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency>
Spring 集成配置方式
- <!-- Spring 配置 c3p0 数据源 -->
- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
- <property name="jdbcUrl" value="${jdbc.url}" />
- <property name="driverClass" value="${jdbc.driverClassName}" />
- <property name="user" value="${jdbc.username}" />
- <property name="password" value="${jdbc.password}" />
- <!-- 连接池中保留的最大连接数。Default: 15 -->
- <property name="maxPoolSize" value="100" />
- <!-- 连接池中保留的最小连接数。-->
- <property name="minPoolSize" value="1" />
- <!-- 初始化时获取的连接数,取值应在 minPoolSize 与 maxPoolSize 之间。Default: 3 -->
- <property name="initialPoolSize" value="10" />
- <!-- 最大空闲时间,60 秒内未使用则连接被丢弃。若为 0 则永不丢弃。Default: 0 -->
- <property name="maxIdleTime" value="30" />
- <!-- 当连接池中的连接耗尽的时候 c3p0 一次同时获取的连接数。Default: 3 -->
- <property name="acquireIncrement" value="5" />
- <!--JDBC 的标准参数,用以控制数据源内加载的 PreparedStatements 数量。Default: 0-->
- <property name="maxStatements" value="0" />
- <!-- 每 60 秒检查所有连接池中的空闲连接。Default: 0 -->
- <property name="idleConnectionTestPeriod" value="60" />
- <!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
- <property name="acquireRetryAttempts" value="30" />
- <!-- 获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。Default: false-->
- <property name="breakAfterAcquireFailure" value="true" />
- <!-- 因性能消耗大请只在需要的时候使用它。Default: false -->
- <property name="testConnectionOnCheckout" value="false" />
- </bean>
Tomcat 中 context.xml 文件 JNDI 配置方式
- <Resource
- name="jdbc/MysqlDataSource"
- auth="Container"
- factory="org.apache.naming.factory.BeanFactory"
- type="com.mchange.v2.c3p0.ComboPooledDataSource"
- driverClass="com.mysql.jdbc.Driver"
- idleConnectionTestPeriod="60"
- maxPoolSize="50"
- minPoolSize="2"
- acquireIncrement="2"
- user="root"
- password="root"
- jdbcUrl="jdbc:mysql://localhost:3306/test"/>
Java 代码中获取 JNDI 数据源
//1、初始化名称查找上下文
Context ctx =new InitialContext();
//2、通过 JNDI 名称找到 DataSource
DataSource ds= (DataSource) ctx.lookup("java:comp/env/jdbc/MysqlDataSource");
//3、通过 ds 获取数据库连接对象
Connectionconn = ds.getConnection();
4. 总结
本文所比较的 5 种数据库连接池在性能方面,根据个人测试结果和参考网上资料 Druid > TomcatJDBC > DBCP > C3P0,BoneCP 的性能方面没有深入比较,应该和 Tomcat Jdbc 差不多。对于小型的系统,并发压力不大时,选择哪一种数据库连接池差别不会很大,主要考虑的应该是连接池的稳定性。当并发量较高时,一般不会选择使用 DBCP 和 C3P0,选择 Druid 是较好的。本文给出了 5 种数据库连接池通过 Spring 配置和 Tomcat JNDI 方式配置两种方式,Spring 配置一般使用单独的属性文件,每一个连接池都提供了使用代码创建的方式,使用方式也比较类似,感兴趣可以自行研究。另外连接不同的数据库时,在配置方面的差异主要在 driverClass 和 jdbcUrl 两项,优化配置项可以另行考虑。
欢迎大家关注博主订阅号“Java 技术日志”,提供 Java 相关技术分享,从 Java 编程基础到 Java 高级技术,从 JavaWeb 技术基础 Jsp、Servlet、JDBC 到 SSH、SSM 开发框架,从 REST 风格接口设计到分布式项目实战。剖析主流开源技术框架,用亲身实践来谱写深度 Java 技术日志。