手写MySQL数据库连接池

一、数据库连接池

在内部对象池中,维护一定数量的数据库连接,并对外暴露数据库连接的获取和返回方法。
如外部使用者可通过getConnection方法获取数据库连接,使用完毕后再通过releaseConnection方法将连接返回,注意此时的连接并没有关闭,而是由连接池管理器回收,并为下一次使用做好准备。

二、线程池作用

  1. 资源重用
    由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,增进了系统环境的平稳性(减少内存碎片以级数据库临时进程、线程的数量)
  2. 更快的系统响应速度
    数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池内备用。此时连接池的初始化操作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
  3. 新的资源分配手段
    对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接技术。
  4. 统一的连接管理,避免数据库连接泄露
    在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用的连接,从而避免了常规数据库连接操作中可能出现的资源泄露。

三、常用数据库连接池

  1. C3P0
    C3P0是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。
  2. BoneCP
    BoneCP 是一个开源的快速的 JDBC 连接池。BoneCP很小,只有四十几K(运行时需要log4j和Google Collections的支持,这二者加起来就不小了),而相比之下 C3P0 要六百多K。另外个人觉得 BoneCP 有个缺点是,JDBC驱动的加载是在连接池之外的,这样在一些应用服务器的配置上就不够灵活。当然,体积小并不是 BoneCP 优秀的原因,BoneCP 到底有什么突出的地方呢,请看看性能测试报告。池,Tomcat的数据源使用的就是DBCP。目前 DBCP 有两个版本分别是 1.3 和 1.4。1.3 版。
  3. DBCP
    DBCP (Database Connection Pool)是一个依赖Jakarta commons-pool对象池机制的数据库连接本对应的是 JDK 1.4-1.5 和 JDBC 3,而1.4 版本对应 JDK 1.6 和 JDBC 4。因此在选择版本的时候要看看你用的是什么 JDK 版本了,功能上倒是没有什么区别。
  4. Proxool
    Proxool是一个Java SQL Driver驱动程序,提供了对你选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中。完全可配置。快速,成熟,健壮。可以透明地为你现存的JDBC驱动程序增加连接池功能。
  5. DBCP与C3P0区别
    dbcp没有自动的去回收空闲连接的功能 c3p0有自动回收空闲连接功能 两者主要是对数据连接的处理方式不同!C3P0提供最大空闲时间,DBCP提供最大连接数。 前者当连接超过最大空闲连接时间时,当前连接就会被断掉。DBCP当连接数超过最大连接数时,所有连接都会被断。

四、写一个自己的连接池

1. 实现思路:

首先创建两个容器,分别为空闲连接容器和活动连接容器。当初始化时,先在空闲连接容器中初始化一定的连接对象,当用户需要连接对象时,先判断空闲容器中是否存有连接对象,如果存有连接对象便将其中一个对象移到连接容器中并返还给用户,不存在则新建对象并返还给用户。当用户调用销毁连接时,首先判断空闲容器的连接对象数有没有达到最大空闲容量,如果没有达到,则该对象不销毁而是放到空闲容器中复用,如果达到最大空闲容量则销毁对象。如果用户获取连接对象时已达到最大连接数,则等待重试。

2. 开始实施

  1. DbConfig配置类
public class DbConfig {
    public static final String driverName = "com.mysql.jdbc.Driver";

    public static final String url = "jdbc:mysql://192.168.2.115:3306/db1";

    public static final String userName = "root";

    public static final String password = "root";

    public static final int minConnections = 1; // 空闲池,最小连接数

    public static final int maxConnections = 50; // 空闲池,最大连接数

    public static final int initConnections = 5;// 初始化连接数

    public static final long connTimeOut = 1000;// 重复获得连接的频率

    public static final int maxActiveConnections = 100;// 最大允许的连接数,和数据库对应

    public static final long connectionTimeOut = 1000 * 60 * 20;// 连接超时时间,默认20分钟
}
  1. ConnectionPool接口
public interface ConnectionPool {
	//获取连接
    public Connection getConnection();
	//销毁连接
    public void releaseConnection(Connection connection);
}
  1. 具体逻辑
public class ConnectionPoolImpl implements ConnectionPool {
    // 空闲连接容器
    private List<Connection> freeConnection = new Vector<Connection>();
    // 活动连接容器
    private List<Connection> activeConnection = new Vector<Connection>();

    //当前连接数
    private AtomicInteger countConne = new AtomicInteger(0);

    public ConnectionPoolImpl() {
        init();
    }

    private void init() {
        //初始化连接放到空闲容器中
        if (DbConfig.initConnections > 0) {
            for (int i = 0; i < DbConfig.initConnections; i++) {
                AddOneConnectToFree();
            }
        }
    }

    private void AddOneConnectToFree(){
        Connection connection = newConnection();
        if (connection!=null){
            freeConnection.add(connection);
        }else {
            AddOneConnectToFree();
        }
    }

    private Connection newConnection(){
        try{
            Class.forName(DbConfig.driverName);
            Connection connection = DriverManager.getConnection(DbConfig.url,DbConfig.userName,DbConfig.password);
            return connection;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    public synchronized Connection getConnection() {
        try{
            Connection connection = null;
            //判断是否大于最大连接数
            if (countConne.get() < DbConfig.maxActiveConnections){
                //判断空闲容器是否有连接对象
                if (freeConnection.size() > 0){
                    connection = freeConnection.remove(0);
                }else {
                    //当空闲容器没有对象时新建对象
                    connection = newConnection();
                }

                if (connection != null && !connection.isClosed()){
                    //将连接对象存到连接容器中
                    activeConnection.add(connection);
                    countConne.getAndIncrement();
                }else {
                    //如果对象不可用,递归重试
                   connection = getConnection();
                }

            }else {
                //当达到最大连接时,等待后重试
                wait(DbConfig.connTimeOut);
                connection = getConnection();
            }

            return connection;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    public synchronized void releaseConnection(Connection connection) {
        try{
            if (connection != null && !connection.isClosed()){
                //判断空闲容器是否达到最大连接
                if (freeConnection.size() < DbConfig.maxConnections){
                    //小于最大连接,则添加到空闲容器中
                    freeConnection.add(connection);
                }else {
                    //大于最大连接直接关闭
                    connection.close();
                }
                activeConnection.remove(connection);
                countConne.getAndDecrement();
            }
            notifyAll();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

3. 测试

public class Test2 {
    public static void main(String[] args) {
        ConnectionPool connectionPool = new ConnectionPoolImpl();
        for (int i = 0; i <= 1000; i++) {
            new Thread(()->{
                for (int j=0;j<5;j++){
                    Connection connection = connectionPool.getConnection();
                    System.out.println(Thread.currentThread().getName() + ",connection:" + connection);
                    connectionPool.releaseConnection(connection);
                }
            }).start();
        }
    }
}

运行之后,便发现已经有复用出现了:

Java mariadb 连接池设置 连接池mysql_Java mariadb 连接池设置