一、基本概念
1.数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
2. 数据库连接池(Connection pooling)是程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对池中的连接进行申请,使用,释放
3. 创建数据库连接是一个很耗时的操作,也容易对数据库造成安全隐患。所以,在程序初始化的时候,集中创建多个数据库连接,并把他们集中管理,供程序使用,可以保证较快的数据库读写速度,还更加安全可靠
4.连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等
5. 连接池最小连接和最大连接
1)数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中, 这些数据库连接的数量是由最小数据库连接数来设定的.无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量
2)连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中
6. 连接池的优点:
1)减少连接创建时间。连接池中的连接是已准备好的、可重复使用的,获取后可以直接访问数据库,因此减少了连接创建的次数和时间
2)简化的编程模式。当使用连接池时,每一个单独的线程能够像创建一个自己的JDBC连接一样操作,允许用户直接使用JDBC编程技术
3)控制资源的使用。如果不使用连接池,每次访问数据库都需要创建一个连接,这样系统的稳定性受系统连接需求影响很大,很容易产生资源浪费和高负载异常。连接池能够使性能最大化,将资源利用控制在一定的水平之下。连接池能控制池中的连接数量,增强了系统在大量用户应用时的稳定性
7. 连接池工作原理:
1)连接池的建立。一般在系统初始化时,连接池会根据系统配置建立,并在池中创建了几个连接对象,以便使用时能从连接池中获取。连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。Java中提供了很多容器类可以方便的构建连接池,例如Vector、Stack等
2)连接池的管理。连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其管理策略是
(1)当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没达到就重新创建一个连接给请求的客户;如果达到就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常给客户
(2)当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过就从连接池中删除该连接,否则保留为其他客户服务
该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销
3)连接池的关闭。当应用程序退出时,关闭连接池中所有的连接,释放连接池相关的资源,该过程正好与创建相反
二、常见配置
1. 基本配置
1)driverClassName 数据库驱动名
2)url 连接数据库的url,不同数据库不一样
3)username 用户名,一般为root
4)password 连接数据库的密码
2. 关键配置
1)minIdle 最小空闲连接
2)maxIdle 最大空闲连接,好像已弃用,用下面的
3)maxActive 最大连接池数量
4)maxWait 获取连接时最大等待时间,单位毫秒
5)initialSize 初始化时建立物理连接的个数
3. 性能配置
1)预缓存设置
(1)poolPreparedStatements 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭
(2)maxOpenPreparedStatements 单个连接拥有的最大缓存数
2)连接有效性检测设置
(1)testOnBorrow 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
(2)testWhileIdle 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效
(3)timeBetweenEvictionRunsMillis 空闲时间,供上面使用
(4)validationQuery 用来检测连接是否有效的sql,要求是一个查询语句
(5)testOnReturn 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
三、分类
1. c3p0
1)开源的JDBC连接池,实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等。单线程,性能较差,适用于小型系统,代码600KB左右
2)重点配置
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
class="com.mchange.v2.c3p0.ComboPooledDataSource
2. dbcp
1)由Apache开发的一个Java数据库连接池项目, Jakarta commons-pool对象池机制,Tomcat使用的连接池组件就是DBCP
2)单独使用dbcp需要3个包:common-dbcp.jar,common-pool.jar,common-collections.jar,预先将数据库连接放在内存中,应用程序需要建立数据库连接时直接到连接池中申请一个就行,用完再放回
3)单线程,并发量低,性能不好,适用于小型系统
4)重点配置
<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>
class="org.apache.commons.dbcp.BasicDataSource
3. Tomcat Jdbc Pool
1)Tomcat在7.0以前都是使用common-dbcp做为连接池组件,但是dbcp是单线程,为保证线程安全会锁整个连接池,性能较差,dbcp有超过60个类,也相对复杂
2)Tomcat从7.0开始引入了新增连接池模块叫做Tomcat jdbc pool,基于Tomcat JULI,使用Tomcat日志框架,完全兼容dbcp,通过异步方式获取连接,支持高并发应用环境,超级简单核心文件只有8个,支持JMX,支持XA Connection
3)重点配置
<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>
class="com.jolbox.bonecp.BoneCPDataSource"
4. boneCP
1)官方说法BoneCP是一个高效、免费、开源的Java数据库连接池实现库。设计初衷就是为了提高数据库连接池性能,根据某些测试数据显示,BoneCP的速度是最快的,要比当时第二快速的连接池快25倍左右,完美集成到一些持久化产品如Hibernate和DataNucleus中
2)BoneCP特色:高度可扩展,快速;连接状态切换的回调机制;允许直接访问连接;自动化重置能力;JMX支持;懒加载能力;支持XML和属性文件配置方式;较好的Java代码组织,100%单元测试分支代码覆盖率;代码40KB左右
3)重点配置
<dependency>
<groupId>com.jolbox</groupId>
<artifactId>bonecp-spring</artifactId>
<version>0.8.0.RELEASE</version>
</dependency>
class="com.jolbox.bonecp.BoneCPDataSource"
5. druid
1)Druid是Java语言中最好的数据库连接池,Druid能够提供强大的监控和扩展功能,是一个可用于大数据实时查询和分析的高容错、高性能的开源分布式系统,尤其是当发生代码部署、机器故障以及其他产品系统遇到宕机等情况时,Druid仍能够保持100%正常运行
2)主要特色:为分析监控设计;快速的交互式查询;高可用;可扩展;Druid是一个开源项目,源码托管在github上
3)重点配置
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.28</version>
</dependency>
class="com.alibaba.druid.pool.DruidDataSource"
性能方面:hikariCP(boneCP)>druid>tomcat-jdbc>dbcp>c3p0 (hikariCP的高性能得益于最大限度的避免锁竞争)
四、自定义数据库连接池
1. 大致步骤
1)实现DataSource接口
2)在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中
3)实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户
4)当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到LinkedList中,而不要把conn还给数据库。Collection保证将自己返回到LinkedList中是此处编程的难点(使用代理,关闭的方法直接通过代理来写)
2. 代码实现
1)JdbcUtil类(取连接以及归还连接,写在一个方法里面)
public class JdbcPool implements DataSource{
private static LinkedList<Connection> listConnections = new LinkedList<>();
static{
InputStream in = JdbcPool.class.getClassLoader().getResourceAsStream("db.properties");
Properties prop = new Properties();
try {
prop.load(in);
String driver = prop.getProperty("driverClassName");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
// 初始化数据库连接大小
int jdbcPoolInitSize = Integer.parseInt(prop.getProperty("jdbcPoolInitSize"));
// 加载数据库驱动
Class.forName(driver);
for(int i=0; i<jdbcPoolInitSize; i++){
Connection conn = DriverManager.getConnection(url,username,password);
System.out.println("获取到了连接" + conn);
// 将获取到的数据库;连接加入到list中
listConnections.add(conn);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public Connection getConnection() throws SQLException {
if(listConnections.size() > 0){
// 取出连接(可能也是为了防止并发用同一个连接?)
final Connection conn = listConnections.removeFirst();
System.out.println("listConnections现在的连接池大小是" + listConnections.size());
return (Connection) Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), new Class[]{Connection.class}, new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 如果调用的不是关闭方法,则直接用
if(!method.getName().equals("close")){
return method.invoke(conn, args);
}else{ // 如果是关闭方法,不能让他关闭,要返回给连接池(要不然所谓池的减少资源消耗作用就没有起到了,)
listConnections.add(conn);
System.out.println(conn + "被还给listConnections数据库连接池了!!");
System.out.println("listConnection还完以后连接池大小为" + listConnections.size());
}
return null;
}
});
}
return null;
}
}
2)JdbcUtil(统一释放资源)
public class JdbcUtil {
/**
* 数据库连接池对象
* */
private static JdbcPool pool = new JdbcPool();
public static Connection getConnection() throws SQLException {
return pool.getConnection();
}
public static void release(Connection conn, Statement st, ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
}
rs=null;
}
if(st != null){
try {
st.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
3. JdbcMain(测试类)
public class JdbcMain {
public static void main(String[] args) throws SQLException {
Connection conn1 = JdbcUtil.getConnection();
Connection conn2 = JdbcUtil.getConnection();
String sql = "select * from user";
Statement stmt1 = conn1.createStatement();
Statement stmt2 = conn2.createStatement();
ResultSet rs1 = stmt1.executeQuery(sql);
ResultSet rs2 = stmt2.executeQuery(sql);
while(rs1.next()){
System.out.println("1111姓名:" + rs1.getString("name")+" 城市:"+rs1.getString("city"));
}
while(rs2.next()){
System.out.println("2222姓名:" + rs2.getString("name")+" 地址:"+rs2.getString("address"));
}
JdbcUtil.release(conn1, stmt1, rs1);
JdbcUtil.release(conn2, stmt2, rs2);
}
}
参考网址
注:文章是经过参考其他的文章然后自己整理出来的,有可能是小部分参考,也有可能是大部分参考,但绝对不是直接转载,觉得侵权了我会删,我只是把这个用于自己的笔记,顺便整理下知识的同时,能帮到一部分人。
ps : 有错误的还望各位大佬指正,小弟不胜感激