5

连接池如何做保活性检测(BeeCP)

 3 years ago
source link: https://my.oschina.net/u/3918073/blog/5036936
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
连接池如何做保活性检测(BeeCP) - 欧德的个人空间 - OSCHINA - 中文开源技术交流社区

前段时间有网友发来信息问:您的连接池怎么没有保活性检测呢?  其实小蜜蜂连接池不但有,而且还技高一筹呢,我们的检测方式是获取到连接后的第一时间进行存活性检测的,如果检测无效,则会主动从池中移除该连接,并尝试获取下一个连接,再检测,一直取到有效的连接或超时退出;第一时间的检测在一定程度保证了池的性能方面的优势【比如国际大牌某池的做法: 从队列获到一个连接后,退出borrow方法,检测结果发现连接无效,则会再次进入borrow方法,我相信有一定的功底朋友能对比看出孰优孰劣(我希望我的代码不仅仅体现出卓越的性能,更重要的要体现出严谨性,优雅性)】小蜜蜂池的思想很精炼,代码方式不搞弯弯绕绕:除非我超时,否则我必须拿到有效的连接。话题有点扯远了,继续回到我们的中心话题。

一:连接存活性检测方式

熟悉连接池的朋友,都知道连接池一般都有连接存活性检测手段,常见的方式有

A: Connection.IsValid方法(JDK1.6以后)

B: 利用Statement执行一段SQL语句(Java1.5之前)

一般性连接池都支持上述两种方式,若驱动不支持第一种方式时,连接池会按第二种方式:间隔一段时间的方式执行某段SQL语句,若无异常则判定为有效连接,所以给连接池提供测试性脚本是非常有必要的(保底性方式),测试代码的大体如下:

public boolean isAlive(Connection con,String sql){
   Statement st=null;
   try{
       st =con.createStatement();
       st.execute(sql);
       return true;
    }catch(Exception e){
      return false;
    }finally{
      if(st!=null)try{st.close();}catch(Exception e){}
    }
}

二:SQL与事务回滚

看过上述代码的朋友,也许会有下面疑问

A:这个SQL使用什么样的语句好呢?

回答:通常是一条简单性的查询语句,有的池默认语句:SELECT 1 或 SELECT 1 FROM DUAL

B:这条语句能是一条insert语句或update语句吗?

 回答:当然可以,有的池没有限定这条语句到底写成啥样。

C: 如果是一条insert语句,连接池长时间运行后,肯定会反复执行这条SQL,那么某表不会膨胀很大?

 回答:是的,如果不加上某些控制,长时间运作某表是有可能造成膨胀现象,这是一个很好的问题。

针对问题C, 我个人的观点是加入事务控制,避免来 ‘真’的(实际性改变),那么上述代码就需要调整为:

public boolean isAlive(Connection con,String sql){
   Statement st=null;
   boolean changed=false;

   try{
       if(con.getAutoCommit()){
          con.setAutoCommit(false);
          changed=true;
       }

       st =con.createStatement();
       st.execute(sql);
       return true;
    }catch(Exception e){
      return false;
    }finally{
      con.rollback();//回滚操作,必须放在finally中
      if(st!=null)try{st.close();}catch(Exception e){}
      if(changed) con.setAutoCommit(true);
    }
}

看了上述代码的朋友也许会提出: rollback为啥要写在finally中呢,直接跟在 execute方法后面不好吗?

我的回答:失败未必没有写入数据,finally是最佳位置处,我们来设想假如用户配置如下一条SQL:

select xxx() from dual

熟悉Oracle的朋友对这样的写法不陌生吧,利用一条SQL语句去执行一个存储过程,在这个基础上,我们把问题继续往前推进一步:假如这个过程中执行了100条insert语句,当执行完99条之后,最后一条insert失败了又或者全部成功呢.  亲爱的朋友们请告诉我,这种情况下是否需要回滚呢? 答案是肯定的,除非你想往库里加点料。

三: 事务切换性问题(追加点料

setAutoCommit朋友都知道这个是用于事务方面的方法,但是您知道:这个方法不能随便切换吗?随意切换会有什么后果?我的代码(https://github.com/Chris2018998/BeeCP/blob/master/src/main/java/cn/beecp/pool/ProxyConnectionBase.java)的83行代码:在 setAutoCommit方法中有一个脏标记检查异常,也许您会很好奇,为啥这样做呢? 我的回答是:数据保护的严谨性,在这种情况下,要么回滚,要么提交,否则不允许改变。下面我们做一个测试,请看下面步骤图(Oracle11g)

第一步:查看数据某表记录(空表)

up-290c3c5099f3624721dd637343f144adc0c.png

第二步:编写测试代码(AutoComit 从false切换到true, 中间不回滚)

up-f803810ae19836ef7f0ec326bf235d6d47e.png

第三步:查看表结果(切换后的结果,被保存进库了)

up-79b75bb32380e21d0d7ce84154338a92655.png

(这个测试主要提醒大家,连接上的赃操作,不小心的话,后续的commit或关闭时,会保存存进库里)

最后祝社区的朋友们五一节快乐!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK