数据库连接池怎么实现_java数据库连接池原理

数据库连接池怎么实现_java数据库连接池原理数据库连接池1.数据库连接池是干什么的假如我们有个应用程序需要每隔10秒查询一次数据库,我们可以用以下方式方法1:每次查询的时候都新建一个数据库连接,查询结束关闭数据库连接。由于数据库连接的建立是一个非常耗费资源的过程,所以这种每次都新建连接的方式非常浪费资源,不可取。方法2:在最开始的新建一个数据库连接,后续过程中一直使用这个数据库连接进行查询,直到最后关

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

数据库连接池

1. 数据库连接池是干什么的

假如我们有个应用程序需要每隔10秒查询一次数据库,我们可以用以下方式

方法1:每次查询的时候都新建一个数据库连接,查询结束关闭数据库连接。

由于数据库连接的建立是一个非常耗费资源的过程,所以这种每次都新建连接的方式非常浪费资源,不可取。

方法2:在最开始的新建一个数据库连接,后续过程中一直使用这个数据库连接进行查询,直到最后关闭

这种方式虽然减少了新建数据库的资源消耗。但是对于一个数据库连接,每10秒才使用一次,也是非常大的浪费。而且假如有一千个应用程序在同时运行,难道就要同时打开一千个数据库连接?

方法3:我们在开始的时候根据需要同时打开多个数据库连接放到一个容器中,每次应用程序进行查询的时候从容器中取出一个数据库连接进行查询,查询完毕放回容器。

方法3即没有重复新建数据库连接,也保证了每个数据库连接的使用率,其中所说的容器就是数据库连接池。

2. 数据库连接池的功能

数据库连接池不仅仅是一个数据库连接的容器,还应具有更加智能的管理数据库连接的功能。我们实现的数据库连接具有以下功能:

  1. 通过getConnection()获取一个数据库连接,如果池中存在连接则直接返回,如果池中没有连接,则新创建一个数据库连接并返回。
  2. 获取的数据库连接进行close()操作的时候,如果连接池中连接数量小于capacity,则该连接自动返回到连接池中,否则直接释放以节省资源。
  3. 即使连接池中的连接数量没有超过capacity,但当其中的部分连接很长时间没有使用时,也要进行释放以节约资源。

3. 数据库连接池中容器的设计

连接池中数据库连接存放的方式可以用队列存放,先放进来的先取出来,也可以用栈来存放,先放进来的后取出来,具体用那种方式,要看需要实现的功能

根据要实现的第三种功能得出,我们需要在存放数据库连接的时候记录连接的上一次使用时间,如果上一次使用时间超过一定时间,则关闭此连接。

如果我们使用队列来存储连接,我们会发现每次新放入的连接都放到了队尾,每次取出来的都是队列前面的最老的数据库连接,所以在不断的存取的过程中,队列里面每一个连接的上一次使用时间都会不断刷新。

一个使用队列的连接池如果有十个连接,每隔10秒取出一个连接使用并放回,则连接池的连接队列会不断从头部取出来,刷新使用时间,放回队列尾部,这样队列每一个连接的上次使用时间都不会超过当前10秒,队列里面所有连接都不能因为超时被释放。

所以我们应当使用栈来存放数据库连接,每次都从上面取出连接,使用完也放回上面。假如使用的频率特别低会导致栈底部的连接长时间未使用,则可以直接释放以节省资源。

连接容器中超时连接的释放有两种方式,1、在往容器中添加或者取出连接的时候释放,2、单独开一个线程不断轮询所有连接释放超时的连接。

我们采用的是第一种方式,在往容器中添加连接的时候释放超时连接,有以下三个原因:

  1. 单独开一个线程需要耗费更多的资源,也更加难以管理
  2. 使用栈来存储连接的话,实际上在不断的存取过程中,栈一直保持着从顶部到底部上次使用时间越来越长的规律,即栈中连接的使用时间是有序的。所以每次释放的时候,只需要从底部向上开始扫描,遇到超时的连接则进行释放,遇上非超时的连接则停止扫描,如果栈中连接均未超时,则只需要扫描最后一个就可以了。
  3. 使用数据库连接的时候,取出来使用应当比放回去的优先级更高,所以释放超时连接的操作应当放在放回连接池中的部分。
  4. 这种方法最坏的情况为:程序开始运行时打开了若干个数据库连接,放置回连接池中,后面则不再进行任何数据库操作(即不再往连接池中取出或存放连接)。这样会导致之前建立的连接一直存放在连接池中,得不到超时释放。但是这种情况出现的几率较少,严格来说这种情况可以通过程序编写避免,所以为了简单和稳定性可以忽略这种情况。

4.数据库连接池的实现

数据库连接池中栈容器的实现是基于Java自带的双向链表来实现的。

public class ConnectionStack{ 
   
    private final int timeout;//单位:秒
    private final int capacity;
    private LinkedList<ConnWithTime> list=new LinkedList<>();
    //内部类,用来记录Connection的上次使用时间
    private class ConnWithTime{ 
   
        private final long time=new Date().getTime();//实例化的时间即为上次归还的时间
        private Connection conn;
        public ConnWithTime(Connection conn){
            this.conn=conn;
        }
        //当前连接是否已经超时
        boolean isTimeout(){
            return new Date().getTime()-time>timeout*1000;
        }
    }
    public ConnectionStack(int capacity,int timeout){
        this.capacity=capacity;
        this.timeout=timeout;
    }
    public synchronized void push(Connection conn){
        clearTimeoutConnection();
        if(list.size()<capacity){
            list.addFirst(new ConnWithTime(conn));
        }else{
            connection.close();
        }
    }
    private void clearTimeoutConnection(){
        //接下来从栈底部开始遍历,把所有超时的连接关闭并且丢弃,直到未超时的连接为止
        //ps:在正常情况下,栈底部最后一个也是活跃的,所以直接break了,平常调用应该是常数时间
        Iterator<ConnWithTime> iterator = list.descendingIterator();
        while (iterator.hasNext()) {
            ConnWithTime connWithTime = iterator.next();
            if (connWithTime.isTimeout()) {
                connWithTime.conn.close();
                iterator.remove();
            } else {
                break;
            }
        }
    }
    public synchronized Connection pop(){
        if(list.isEmpty())return null;
        return list.removeFirst().conn;
    }

    public synchronized void close(){
        Iterator<ConnWithTime> iterator = list.descendingIterator();
        while (iterator.hasNext()) {
            ConnWithTime connWithTime = iterator.next();
            connWithTime.conn.close();
            iterator.remove();
        }
}

数据库连接池的主体部分实现应当如下:

public class DatabasePool{ 
   
    private ConnectionStack stack;
    private final String url,username,password;
    private volatile boolean isRun=true;

    public DatabasePool(String url, String username, String password, int capacity, int timeout) {
       this.url = url;
       this.username = username;
       this.password = password;
       stack = new ConnectionStack(capacity, timeout);
    }

    public Connection getConnection() throws SQLException{
        if(!isRun)throw new SQLException("pool closed");
        Connection conn=stack.pop();
        if(conn==null)conn= DriverManager.getConnection(url, username, password);
        final myConn=conn;
        //使用动态代理,当调用Connection.close()的时候自动将连接放回连接池中
        return (Connection) Proxy.newProxyInstance(
                    DatabasePool.class.getClassLoader(),
                    conn.getClass().getInterfaces(),
                    (proxy, method, args) -> {
                        if (method.getName().equals("close") && isRun) {
  
  //当为调用close函数并且连接池正在运行,则将连接放入stack中,否则直接调用相关函数
                            stack.push(myConn);//
                        } else {
                            return method.invoke(myConn, args);
                        }
                        return null;
                    });
    }
    public void close(){
        isRun=false;
        stack.close();
    }
}

5. 数据库连接池的使用

数据库连接池是线程安全的,所以即使在多线程条件下可以直接使用

DatabasePool pool=new DatabasePool(url,username,password,10,60*60);
//多个线程下可以同时如下使用
Connection conn=null;
try{
    conn=pool.getConnection();
    ...//执行数据库操作 
}catch(SQLException e){
    logger.exception(e);
}finally{
    if(conn!=null)conn.close();
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/194119.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 苹果屏蔽更新描述文件_屏蔽iPhone更新的iOS描述文件安装办法「建议收藏」

    屏蔽iOS更新分两种,第一种非越狱设备,只能系统在12.1以下的才能安装屏蔽描述文件safari浏览器输入ibeta.me回车,找到下图的屏蔽OTA更新,直接安装就行了第二种越狱设备,首先添加源https://xsf1re.github.io/repo/安装下图这个插件这个文件可以解除12.1以上设备描述文件失效不允许安装的问题,作者说写着兼容12.4-13.3,其他系统的老铁们自…

    2022年4月11日
    458
  • 快速教会你,虚拟机如何上网

    快速教会你,虚拟机如何上网虚拟机如何上网###在真机里[kiosk@foundationDesktop]$su-##切换到超级用户电脑不联网。先检查一下是否有网关,如果有网关的话需要先删除,以下操作步骤是给真机还原一个初始环境[root@foundation5~]#route-n##…

    2022年5月19日
    43
  • 独立成分分析(Independent Component Analysis,ICA)原理及代码实现

    独立成分分析(Independent Component Analysis,ICA)原理及代码实现过程监控中会用到很多中方法,如主成分分析(PCA)、慢特征分析(SFA)、概率MVA方法或独立成分分析(ICA)等为主流算法。其中PCA主要多用于降维及特征提取,且只对正太分布(高斯分布)数据样本有效;SFA被用来学习过程监控的时间相关表示,SFA不仅可以通过监测稳态分布来检测与运行条件的偏差,还可以根据时间分布来识别过程的动态异常,多用于分类分析;概率MVA方法,多以解决动力学、时变、非线性等问题。今天要介绍的是独立成分分析(ICA),由浅入深,细细道来。此外文末还附有ICA可实现的代码哟~不要错过

    2022年5月17日
    49
  • Shiro 框架简单介绍

    Shiro 框架简单介绍Shiro 框架简单介绍

    2022年4月22日
    43
  • navicat永久激活码2021(最新序列号破解)

    navicat永久激活码2021(最新序列号破解),https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月18日
    1.3K
  • 连接查询和子查询哪个效率高

    连接查询和子查询哪个效率高需要进行多表查询的情况下,用连接查询和子查询哪个效率高?1、什么是子查询?举个简单的例子,那么子查询有什么优劣呢?子查询(内查询)在主查询之前一次执行完成。子查询的结果被主查询(外查询)使用。可以用一个子查询替代上边的的表名。子查询,将查询操作嵌套在另一个查询操作中。先执行子查询,再执行外查询注:在查询时基于未知的值时,应使用子查询子查询可以返回多个结果/单个结果,结果个数不同应该使用不同的操作符通过子查询不难看出,可以根据employee_id查到department_

    2022年5月29日
    235

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号