数据库常用连接池总结 - 日记屋
数据库常用连接池总结
发布时间:

数据库常用连接池总结

一、为什么需要连接池

数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。  一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的 性能低下。
数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并讲这些连接组成一个连接池,由应用程序动态地对池中的连接进行申请、使用和释放。
对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。
 连接池技术尽可能多地重用了消耗内存地资源,大大节省了内存,提高了服务器地服务效率,能够支持更多的客户服务。通过使用连接池,将大大提高程序运行效率,同时,我们可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。


二、定义

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。


三、工作原理

连接池的工作原理主要由三部分组成,分别为连接池的建立、连接池中连接的使用管理、连接池的关闭。
第一、连接池的建立。一般在系统初始化时,连接池会根据系统配置建立,并在池中创建了几个连接对象,以便使用时能从连接池中获取。连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。Java中提供了很多容器类可以方便的构建连接池,例如Vector、Stack等。
第二、连接池的管理。连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其管理策略是: 当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没达到就重新创建一个连接给请求的客户;如果达到就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常给客户。
当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过就从连接池中删除该连接,否则保留为其他客户服务。
该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销。
 第三、连接池的关闭。当应用程序退出时,关闭连接池中所有的连接,释放连接池相关的资源,该过程正好与创建相反。


四、传统数据库连接对比数据库连接池

数据库常用连接池总结

不使用数据库连接池的步骤:
TCP建立连接的三次握手
MySQL认证的三次握手
真正的SQL执行
MySQL的关闭
TCP的四次握手关闭
可以看到,为了执行一条SQL,却多了非常多我们不关心的网络交互。

优点:实现简单
缺点:

  1. 网络IO较多
  2. 数据库的负载较高
  3. 响应时间较长及QPS较低
  4. 应用频繁的创建连接和关闭连接,导致临时对象较多,GC频繁
  5. 在关闭连接后,会出现大量TIME_WAIT 的TCP状态(在2个MSL之后关闭)

数据库常用连接池总结

使用数据库连接池的步骤:

第一次访问的时候,需要建立连接。 但是之后的访问,均会复用之前创建的连接,直接执行SQL语句。

优点:

较少了网络开销
系统的性能会有一个实质的提升
没了麻烦的TIME_WAIT状态


五、常用连接池对比
目前存在多个开源的java数据库连接池,这些连接池都是在java.sql基础上编写而成。

建数据库test,然后在创建表

CREATE TABLE `user` (
  `userid` INT(11) NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(255) DEFAULT NULL,
  `password` VARCHAR(255) DEFAULT NULL,
  `email` VARCHAR(255) DEFAULT NULL,
  `phone` VARCHAR(255) DEFAULT NULL,
  `status` VARCHAR(255) NOT NULL DEFAULT '0',
  `code` VARCHAR(255) DEFAULT NULL,
  PRIMARY KEY (`userid`)
) ENGINE=INNODB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8;

DBCP连接池
DBCP 是 Apache 软件基金组织下的开源连接池实现,使用DBCP数据源,应用程序应在系统中增加如下两个 jar 文件:
Commons-dbcp.jar:连接池的实现
Commons-pool.jar:连接池实现的依赖库
Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
核心类:org.apache.commons.dbcp2.BasicDataSource extends Object implements DataSource

数据库常用连接池总结

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;
import org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory;
import org.junit.Test;

import com.alibaba.druid.pool.DruidDataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class SqlDriverManage {
    private final String driverClassName = "com.mysql.cj.jdbc.Driver";
    private final String url = "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC";
    private final String userName = "root";
    private final String userPassword = "123456";
    Connection con=null;
    PreparedStatement pst=null;
    ResultSet rs=null;
    /*
     * 使用java.sql.DriverManager类----传统方式,没有使用连接池,需要引入mysql驱动包
     */
    @Test
    public void test1(){
        try {
            //1.驱动注册程序
            Class.forName(driverClassName);
            //2.获取连接对象
            con= DriverManager.getConnection(url,userName, userPassword);
            //3.准备sql
            String sql="select * from user";
            //4.创建prepareStatement
            pst=con.prepareStatement(sql);
            //5.执行sql语句,得到返回结果
            rs= pst.executeQuery();
            //6.遍历结果,索引从1开始
            while(rs.next()){
                System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            //7.关闭连接(顺序:后打开的先关闭)
            if(rs!=null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if(pst!=null){
                try {
                    pst.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if(con!=null){
                try {
                    con.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
    }
/*
     * 第一种方式使用实现了javax.sql.DataSource接口的子类--DBCP连接池硬编码
     * 在上述代码中添加单元测试代码
     */  
    @Test
    public void test2(){

        try {
            //1.驱动注册程序
            BasicDataSource bds=new BasicDataSource();
            bds.setDriverClassName(driverClassName);
            bds.setUrl(url);
            bds.setUsername(userName);
            bds.setPassword(userPassword);
            //2.获取连接对象
            con=bds.getConnection();
            //3.准备sql语句
            String sql="select * from user";
            //4.创建prepareStatement
            pst=con.prepareStatement(sql);
            //5.执行sql语句,得到返回结果
            rs= pst.executeQuery();
            //6.遍历结果,索引从1开始
            while(rs.next()){
                System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3));
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
/*
     *第二种方式使用实现了javax.sql.DataSource接口的子类--DBCP连接池软编码
     */
    @Test
    public void test3(){
        try {
            //1.驱动注册程序
            Properties pro=new Properties();
            BasicDataSource bds=null;
            InputStream inStream=SqlDriverManage.class.getResourceAsStream("dbcp.properties");
            pro.load(inStream);
            bds=new BasicDataSourceFactory().createDataSource(pro);
            //2.获取连接对象
            con=bds.getConnection();
            //3.准备sql语句
            String sql="select * from user";
            //4.创建prepareStatement
            pst=con.prepareStatement(sql);
            //5.执行sql语句,得到返回结果
            rs= pst.executeQuery();
            //6.遍历结果,索引从1开始
            while(rs.next()){
                System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3));
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
#dbcp.properties
driverClassName=com.mysql.cj.jdbc.Driver
username=root
password=123456
url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC

在此过程中出现的问题

数据库常用连接池总结

解决方法 添加tomcat斌目录下的tomcat-juli.jar

数据库常用连接池总结


数据库常用连接池总结

解决方法:

  1. DBCP连接池配置文件中的key与BasicDataSouce中的属性一样
  2. URl后追加allowPublicKeyRetrieval=true

数据库常用连接池总结

C3P0连接池:
最常用的连接池技术!Spring框架,默认支持C3P0连接池技术!
需要引入c3p0-0.9.1.2.jar
核心类:com.mchange.v2.c3p0.ComboPooledDataSource implements PooledDataSource extends DataSource

数据库常用连接池总结

/*
     * 使用实现了javax.sql.DataSource接口的子类--c3p0连接池硬编码
     */
    @Test
    public void test4(){
        try {
            //1.驱动注册程序
            ComboPooledDataSource cpd=new ComboPooledDataSource();
            cpd.setDriverClass(driverClassName);
            cpd.setJdbcUrl(url);
            cpd.setUser(userName);
            cpd.setPassword(userPassword);
            //2.获取连接对象
            con=cpd.getConnection();
            //3.准备sql语句
            String sql="select * from user";
            //4.创建prepareStatement
            pst=con.prepareStatement(sql);
            //5.执行sql语句,得到返回结果
            rs= pst.executeQuery();
            //6.遍历结果,索引从1开始
            while(rs.next()){
                System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3));
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
/*
     * 使用实现了javax.sql.DataSource接口的子类--c3p0连接池配置文件xml
     */
    @Test
    public void test5(){
        try {
            //1.驱动注册程序
            ComboPooledDataSource cpd=new ComboPooledDataSource("mysql-config");
            //ComboPooledDataSource cpd=new ComboPooledDataSource();
            //2.获取连接对象
            con=cpd.getConnection();
            //3.准备sql语句
            String sql="select * from user";
            //4.创建prepareStatement
            pst=con.prepareStatement(sql);
            //5.执行sql语句,得到返回结果
            rs= pst.executeQuery();
            //6.遍历结果,索引从1开始
            while(rs.next()){
                System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3));
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
<!--自动加载src下c3p0的配置文件【c3p0-config.xml】,必须是放在src下。-->
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <default-config>
        <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC</property>
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="user">root</property>
        <property name="password">123456</property>
    </default-config>
    <named-config name="mysql-config">
        <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC</property>
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="user">root</property>
        <property name="password">123456</property>
    </named-config>
</c3p0-config>

在此过程中出现的问题

数据库常用连接池总结

数据库常用连接池总结

DRUID简介
Druid是阿里巴巴开发的号称为监控而生的数据库连接池。可以监控数据库访问性能,Druid内置提供了一个功能强大的StatView插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。
核心类 com.alibaba.druid.pool.DruidDataSource extends DruidAbstractDataSource implements DataSource

数据库常用连接池总结

    /*
     * 使用实现了javax.sql.DataSource接口的子类--druid连接池硬编码
     */
    @Test
    public void test6(){
        try {
            //1.驱动注册程序
            DruidDataSource dds=new DruidDataSource();
            dds.setDriverClassName(driverClassName);
            dds.setUrl(url);
            dds.setUsername(userName);
            dds.setPassword(userPassword);
            //2.获取连接对象
            con=dds.getConnection();
            //3.准备sql语句
            String sql="select * from user";
            //4.创建prepareStatement
            pst=con.prepareStatement(sql);
            //5.执行sql语句,得到返回结果
            rs= pst.executeQuery();
            //6.遍历结果,索引从1开始
            while(rs.next()){
                System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3));
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
/*
     * 使用实现了javax.sql.DataSource接口的子类--druid连接池软编码
     */
    @Test
    public void test7(){
        try {
            //1.驱动注册程序
            Properties pro=new Properties();
            DataSource dds=null;
            InputStream inStream=SqlDriverManage.class.getResourceAsStream("druid.properties");
            pro.load(inStream);
            dds=com.alibaba.druid.pool.DruidDataSourceFactory.createDataSource(pro);

            //2.获取连接对象
            con=dds.getConnection();
            //3.准备sql语句
            String sql="select * from user";
            //4.创建prepareStatement
            pst=con.prepareStatement(sql);
            //5.执行sql语句,得到返回结果
            rs= pst.executeQuery();
            //6.遍历结果,索引从1开始
            while(rs.next()){
                System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getString(3));
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
#druid.properties
driverClassName=com.mysql.cj.jdbc.Driver
username=root
password=123456
url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=truecharacterEncoding=UTF-8&useSSL=false&serverTimezone=UTC

本周热门