实现一个简易的数据库连接池

简介: 实现一个简易的数据库连接池

数据库连接池的作用:#



1. 资源重用


当多个用户频繁的去对数据库进行读写操作时,会不间断的创建Connection,在数据库开始读写数据之前,把资源过多的分配给创建连接释放连接上,这笔开销得不偿失.数据库连接池的对连接Connection的资源回收机制对此做出了优化


2. 更快的系统响应速度


数据库连接池一旦初始化,用户获取的Connection不再创建新的,而是从现有的容器里面取,使得直接利用成为可能,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间


3. 新的资源分配手段


对于多应用共享同一数据库的系统而言 ,某一应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源


4. 统一的连接管理,避免数据库连接泄漏


数据库连接池提供连接超时设定,超时后会重试,针对用户获取到的不健康的Connection,同样会重新分配新的连接,从而避免了常规数据库连接操作中可能出现的资源泄漏


实现一个简单的数据库连接池#


目标:

  • 要实现的数据库连接池,统一注册驱动(替换每一次连接数据库都要注册驱动的时代)
  • 用户想对数据库进行进一步的操作,需要在数据库连接池中获取连接Connection
  • 用户对队数据库读写完毕之后, 连接池回收当前的Connection


两个容器

数据库连接池 = 空闲连接池 + 工作连接池


三个重要的参数

  • 最大连接数
  • 最大连接数是对Connection总数的限制 一般是((核心数 * 2) + 有效磁盘数)
  • 空闲连接数
  • 空闲连接数 表示当前的空闲连接池中的Connection的数量,我们给他规定最大值和最小值
  • 当前值 < 最大连接数 表示用户有机会获取连接
  • 如果 空闲连接池的size>0 直接获取连接
  • 否则 创建一个新的Connection给用户
  • 当前值 >= 最大连接数 Connection的创建达到了上限,用户只能等待重试
  • 工作连接数
  • 工作连接数 表示 当前工作连接池中的Connection的数量


获取连接经历什么?

  1. 空闲连接池中的弹出一个Connection
  2. 把当前的Connection加入到工作连接池


连接是如何被回收的?

  1. 如果空闲连接池未满.直接添加进去
  2. 把工作连接池中相应的连接移除
  3. 如果空闲连接池满了.直接close()掉
  4. 把工作连接池中相应的连接移除


配置文件读取工具类#


public class propertiesUtil {
    private static Properties configObj = new Properties();
    static{
        // 使用类加载器读取的文件要放在src下
        InputStream connectionPool = Thread.currentThread().getContextClassLoader().getResourceAsStream("pool.properties");
        InputStreamReader inputStreamReader = new InputStreamReader(connectionPool);
        try {
            configObj.load(inputStreamReader);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static String getValue(String key){
        return configObj.getProperty(key);
    }
}


定义接口,规范我们的连接池的方法#


public interface myConnectionPool  {
    /**
     * 获取连接
     * @return
     */
    public Connection getConnection();
    /**
     * 释放连接
     * @param connection
     */
    public void releaseConnection(Connection connection);
}


具体的实现类实现myConnectionPool接口#


public class ConnectionPool implements IConnection {
    // 线程安全的两个容器,分别存放空闲线程数和活动的线程数
    private List<Connection> freeConnection = new CopyOnWriteArrayList<>();
    private List<Connection> activeConnection = new CopyOnWriteArrayList<>();
    // 原子类 标记的是 空闲池的存放的连接数
    private AtomicInteger atomicInteger;
    public ConnectionPool() {
        this.atomicInteger = new AtomicInteger(0);
        // 初始化空闲连接池
        init();
    }
    // 初始空闲连接池
    public void init() {
        // 获取连接数,给freeConnection 池添加指定数量的连接数
        for (int i = 0; i < Integer.valueOf(propertiesUtil.getValue("initConnections")); i++) {
            // 创建连接
            Connection connection = newConnection();
            if (null != connection) {
                // 添加到容器
                freeConnection.add(connection);
            }
        }
    }
    // 获取连接
    @Override
    public synchronized Connection getConnection() {
        Connection connection=null;
        // 判断是否达到了最大连接数--> 决定给用户连接还是让他等待
        if (atomicInteger.get()<Integer.valueOf(propertiesUtil.getValue("maxActiveConnetions"))){
            // 当前小于最大的连接数,直接给当前的用户连接
            if (freeConnection.size()>0){ // 空闲线程里面有直接从空闲线程里面取
                connection  = freeConnection.remove(0);
            }else{  // 空闲线程里面没有,直接创建一个新的连接
                connection = newConnection();
            }
            // 判断连接是否可用
             if(isAvailable(connection)){
                 // 添加到一个活动线程里面
                 activeConnection.add(connection);
             }else{ // 如果连接不可用,递归
                 //  如果连接不可用的话,说明有一次newConnection()失败了,我们得 atomicInteger.decrementAndGet(); 把newConnection()里面的原子增加去掉
                 atomicInteger.decrementAndGet();
                 connection = getConnection();
             }
        }else{
            // 等待
            try {
                wait(Integer.valueOf(propertiesUtil.getValue("connTimeOut")));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return connection;
    }
    // 释放本次连接  ; 把本次连接从活动池 转移到 空闲池
    @Override
    public synchronized void releaseConnection(Connection connection) {
        // 判断连接是否可用
        if(isAvailable(connection)){ // 可用
            // 回收
            // 判断空闲池是否满了
            if(freeConnection.size()<Integer.valueOf(propertiesUtil.getValue("maxConnections"))){
                // 未满
                freeConnection.add(connection);
            }else{
                // 满了
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            // 移除出去当前的这个连接
            activeConnection.remove(connection);
            atomicInteger.decrementAndGet();
            // 现在可能有连接正在等待,既然这里释放了,那么就唤醒全部等待的线程
            notifyAll();
        }else{ // 不可用
            throw new RuntimeException("连接回收异常");
        }
    }
    // 创建新的连接
    public Connection newConnection() {
        try {
            // 注册驱动
            Class.forName(propertiesUtil.getValue("driverName"));
            // 获取连接
            Connection connection = DriverManager.getConnection(
                    propertiesUtil.getValue("url"),
                    propertiesUtil.getValue("userName"),
                    propertiesUtil.getValue("password"));
            // 原子增加
            atomicInteger.addAndGet(1);
            return connection;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    // 判断连接是否可用
    public boolean isAvailable(Connection connection){
        try {
            if (null==connection||connection.isClosed()){
                return false;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return true;
    }
}


配置文件#


# 数据库相关
driverName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/text
userName=root
password=root
# 空闲池的 最小数
minConnections=1
# 空闲池的 最大数
maxConnections=10
# 初始化的连接数
initConnections=5
# 本次连接超时时间(重试时间)
connTimeOut=1000
# 最大的连接数
maxActiveConnetions=10
相关文章
|
6月前
|
SQL 网络协议 数据库连接
什么是数据库连接池?为什么需要数据库连接池呢?
什么是数据库连接池?为什么需要数据库连接池呢?
|
网络协议 关系型数据库 MySQL
实现数据库连接池
实现数据库连接池
130 0
|
1天前
|
数据库 数据安全/隐私保护
数据库连接池
myPool.driver:指明数据库的驱动类。 myPool.url:连接数据库的url。 myPool.username: 数据库用户名。 myPool.password:数据库密码。 myPool.initSize:数据库连接池的初始化大小(即初始时有几条默认连接)。 myPool.maxSize:数据库连接池的最大大小(即池中最多能有几条连接)。 myPool.delay=2000:连接池健康检查的开始时间,即当连接池开始工作后多长时间开始连接池的健康检查。 myPool.interval:连接池的健康检查的间隔时间,即每隔多长时间就进行一次连接的健康检查。 myPool.timeou
|
1月前
|
SQL 监控 Java
C3P0数据库连接池
C3P0数据库连接池
|
6月前
|
SQL druid Java
Driud数据库连接池的使用
Driud数据库连接池的使用
80 0
|
5月前
|
SQL 缓存 关系型数据库
数据库连接池到底应该设多大?
数据库连接池到底应该设多大?
293 0
|
6月前
|
Java 关系型数据库 MySQL
数据库连接池的实现
数据库连接池的实现
|
Java 关系型数据库 应用服务中间件
数据库连接池对比
数据库连接池对比
|
Java 数据库连接 数据库
一篇文章学会学会c3p0数据库连接池~
一篇文章学会学会c3p0数据库连接池~
255 0
|
SQL 缓存 监控
数据库连接池 BoneCP、HikariCP 等
数据库连接池 BoneCP、HikariCP 等
239 0