数据库连接池应用中数据库服务器断开超时连接的问题

数据库应用开发过程中,我们可能会遇到一个问题:应用使用了数据库连接池,每经过指定时间后,发出到数据库服务器的任何请求都会失败,而且有且仅有一次失败,之后的正常访问都没有问题。尤其是在Web应用中,如果晚上时段没有访问,而第二天第一个访客的经历就是碰到一个数据库访问错误,如果开发系统的程序员没有注意这个问题的话,可能终端用户访问会看到抛出的一堆数据库异常信息。

其实,这个问题的主要原因是,应用中数据库连接池中会保存指定数量的数据库连接实例,而这些连接实例并没有定时地检测其到数据库服务器连接是否正常;数据库服务器可以配置一个数据库连接实例的超时时间,超过时间后它会自动断开连接。也就是,被断开的那个连接此时仍然保存在应用的数据库连接池内,下次被使用的时候就会发生数据库连接断开而导致一次访问失败。

解决上述连接关闭的方案有两种值得推荐:

  • 如果能够提供这样一种检测机制,在应用的连接池管理中定时地检测连接池中连接的有效性,就完全可以避免上面描述的问题。
  • 在应用代码中通过异常处理机制,来实现该次业务的重新处理,也可以很好地避免。

我们举一个例子,使用Java开发的Web系统,Tomcat作为HTTP服务器,MySQL作为数据库,抛出异常的信息如下所示:

[http-bio-8080-exec-10] 2012-11-28 00:55:43 [org.shirdrn.wm.de.action.StatAction]-[WARN]   
com.ibatis.dao.client.DaoException: Error ending SQL Map transaction.  Cause: java.sql.SQLException: Already closed.  
    at com.ibatis.dao.engine.transaction.sqlmap.SqlMapDaoTransaction.rollback(SqlMapDaoTransaction.java:51)  
    at com.ibatis.dao.engine.transaction.sqlmap.SqlMapDaoTransactionManager.rollbackTransaction(SqlMapDaoTransactionManager.java:85)  
    at com.ibatis.dao.engine.impl.DaoContext.endTransaction(DaoContext.java:112)  
    at com.ibatis.dao.engine.impl.DaoProxy.invoke(DaoProxy.java:77)  
    at $Proxy8.selectByExample(Unknown Source)  
    at org.shirdrn.wm.de.service.impl.StatItemsServiceImpl.countItems(Unknown Source)  
    at org.shirdrn.wm.de.action.StatAction.makeStat(Unknown Source)  
    at org.shirdrn.wm.de.action.StatAction.doGet(Unknown Source)  
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)  
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)  
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)  
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)  
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224)  
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169)  
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)  
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)  
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)  
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)  
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)  
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)  
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:987)  
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:579)  
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:307)  
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)  
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)  
    at java.lang.Thread.run(Thread.java:619)  
Caused by: java.sql.SQLException: Already closed.  
    at org.apache.commons.dbcp.PoolableConnection.close(PoolableConnection.java:84)  
    at org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.close(PoolingDataSource.java:181)  
    at com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransaction.close(JdbcTransaction.java:81)  
    at com.ibatis.sqlmap.engine.transaction.TransactionManager.end(TransactionManager.java:93)  
    at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.endTransaction(SqlMapExecutorDelegate.java:734)  
    at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.endTransaction(SqlMapSessionImpl.java:176)  
    at com.ibatis.sqlmap.engine.impl.SqlMapClientImpl.endTransaction(SqlMapClientImpl.java:153)  
    at com.ibatis.dao.engine.transaction.sqlmap.SqlMapDaoTransaction.rollback(SqlMapDaoTransaction.java:49)  
    ... 25 more

我们通过上面给出的第二种方案来解决,对应异常中实现的代码,进行异常处理的逻辑如下所示:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  
    boolean retry = false;  
    String type = request.getParameter(RequestParams.ITEM_TYPE);  
    String top = request.getParameter(RequestParams.TOP_N);  
    Byte itemType = Byte.parseByte(type);  
    Integer topN = super.topN;  
    if(top!=null) {  
        try {  
            topN = Integer.parseInt(top);  
        } catch (Exception e) {}  
    }  
    Target target = itemNames.get(itemType);  
    try {  
        makeStat(request, response, itemType, topN, target);  
    } catch (Exception e) {  
        LOG.warn("", e);  
        // com.ibatis.dao.client.DaoException: Error ending SQL Map transaction.  Cause: java.sql.SQLException: Already closed.  
        if(!retry && e instanceof DaoException) {  
            LOG.warn("Try to obtain database connection again.");  
            retry = true;  
            this.makeStat(request, response, itemType, topN, target);  
        } else {  
            response.sendError(500, e.toString());  
            return;  
        }  
    }  
    request.getRequestDispatcher(target.url).forward(request, response);  
}  
  
private void makeStat(HttpServletRequest request, HttpServletResponse response,   
        Byte itemType, Integer topN, Target target) throws IOException, ServletException {  
    List<StatItems> items = statItemsService.countItems(itemType, new Date(), topN);  
    for (StatItems statK : items) {  
        if(statK.getItemName()!=null && !"null".equalsIgnoreCase(statK.getItemName())) {  
            pieDataset.setValue(statK.getItemName().trim() + " (" + statK.getPercentage() + ")", statK.getItemValue());  
        }  
    }  
    String imageUrl = super.generateImage(pieDataset, target.title, request);  
    request.setAttribute("items", items);  
    request.setAttribute("imageUrl", imageUrl);  
    if(items!=null && !items.isEmpty() && items.size()<topN) {  
        topN = items.size();  
    }  
    request.setAttribute("topN", topN);  
}

上面代码,判断如果是发生连接失败,则保存请求参数,再重新处理该请求。

另一种不推荐的方案,就是修改数据库服务器的连接超时配置。因为在实际项目中,通常应用上线的相关人员未必是DBA,对于修改数据库服务器的配置可能会给其它上线业务带来风险。解决方法如下:

以MySQL为例,查看文件/etc/my.cnf,查询有关超时配置的参数:

mysql> show variables like ‘%timeout‘;  
+----------------------------+----------+  
| Variable_name              | Value    |  
+----------------------------+----------+  
| connect_timeout            | 10       |  
| delayed_insert_timeout     | 300      |  
| innodb_lock_wait_timeout   | 50       |  
| innodb_rollback_on_timeout | OFF      |  
| interactive_timeout        | 28800    |  
| lock_wait_timeout          | 31536000 |  
| net_read_timeout           | 30       |  
| net_write_timeout          | 60       |  
| slave_net_timeout          | 3600     |  
| wait_timeout               | 28800    |  
+----------------------------+----------+

我们可以在属性组mysqld下面修改如下两个参数:

  • interactive_timeout
  • wait_timeout

MySQL数据库服务器配置的连接超时时间默认是8小时,如果修改的超时时间足够长的话,就不会出现前面发生的连接断开的问题。但是,如果有很多应用都在使用数据库连接池,大量的数据库连接资源一直被占用,严重的话可能使数据库服务器宕机,而且,也会使一些攻击者伪造大量请求,使数据库服务器负荷过载而宕机,从而影响应用处理业务。

时间: 2024-10-12 13:07:56

数据库连接池应用中数据库服务器断开超时连接的问题的相关文章

Lync Server 2013 的 Enterprise Edition 前端池部署中的服务器并置

本节描述可在 Lync Server 2013 前端池部署中并置的服务器角色.数据库和文件共享. 1.服务器角色,在 Lync Server 2013 中,A/V 会议服务.中介服务.监控和存档并置在前端服务器上,但需要进行额外配置才能启用它们.如果不想将中介服务器与前端服务器并置,则可以在单独的计算机上将其部署为独立中介服务器. 可以将受信任应用程序服务器与前端服务器并置.以下服务器角色必须分别部署在不同的计算机上: 控制器边缘服务器中介服务器(若未与前端服务器并置)Office Web Ap

XShell 连接虚拟机中的服务器 失败 、连接中断(Connection closed by foreign host.)

在使用XShell连接虚拟机中的服务器时,报以下错误并断开连接,之前连接还是挺稳定的,忽然就这样了 Last login: Thu Aug 10 21:28:38 2017 from 192.168.1.102 [[email protected] ~]# Socket error Event: 32 Error: 10053. Connection closing...Socket close. Connection closed by foreign host. Disconnected f

[总结]数据库连接池设置不当导致大量的本地连接出现time_wait状态。

最近在做性能测试时,在使用netstat命令查看本地网络连接状态时发现有大量的连接处于time_wait状态. 于是认为是我们的dbcp的配置文件写的有问题,开始查应如何配置dbcp.但是改了几个参数后,发现还是出现大量的time_wait. 于是又开始查看官方的配置说明,还是我们老大认真犀利,发现了这段: If maxIdle is set too low on heavily loaded systems it is possible you will see connections bei

[数据库]服务器监听连接不上

标题 是大部分 都能百度出 解决方案的,但是 就是那么的不巧 百度出的任何方案都不能觉得问题.... 事情发生在上周五的晚上,魔都的天气冻死个人,事情也出现的让人心哇凉哇凉的..... 首先,部里的人 说我们没传数据过去,本来是抵着万般不情愿的心情的,结果肿是那么不近人意,还真是我们的数据库问题啊, TNS:无监听 第一想法:监听程序异常了? 查看了监听及相关配置三大文件,etc文件 都很乖的,很正常的 监听程序也启动着呢, 本地sqlplus也连接正常啊,怎么就jdbc连接不成功呢? 第二想法

关于c3p0数据库连接池的简单使用

在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤: 在主程序(如servlet.beans)中建立数据库连接. 进行sql操作 断开数据库连接. 这种模式开发,存在的问题: 普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证用户名和密码(得花费0.05s-1s的时间).需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接.这样的方式将会消耗大量的资源和时间.数据库的连接资源并没有得

Java -- JDBC 学习--数据库连接池

JDBC数据库连接池的必要性 在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤: 在主程序(如servlet.beans)中建立数据库连接. 进行sql操作 断开数据库连接. 这种模式开发,存在的问题: 普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证用户名和密码(得花费0.05s-1s的时间).需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接.这样的方式将会消耗大量的资源和

【转】JDBC学习笔记(8)——数据库连接池(dbcp&amp;C3P0)

转自:http://www.cnblogs.com/ysw-go/ JDBC数据库连接池的必要性 一.在使用开发基于数据库的web程序时,传统的模式基本是按一下步骤: 1)在主程序(如servlet/beans)中建立数据库连接 2)进行sql操作 3)断开数据库连接 二.这种模式开发,存在的问题: 1)普通的JDBC数据库连接使用DriverManager来获取,每次向数据库建立连接的时候都要将Connection加载进内存中,再验证用户名和密码(得花费0.05s~1s的时间).需要数据库连接

浅析数据库连接池(二)

上一篇博客,主要是简单的介绍了普通数据库连接的过程以及耗费的资源,并且简单的谈了下连接池,这篇我们主要来看看数据库连接池的使用以及它最优的配置. 总目录: -1.数据库连接过程是怎样的? -2.连接所占用的资源有哪些? -3.连接池简介 -4.连接池的使用 -5.最优连接池配置选择 今天主要看看4和5. 4.连接池的使用 这里我使用的是c3p0数据库连接池 简单的介绍一下c3p0:C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展.目前使用

数据库连接池的配置

一.什么是数据库连接池? 官方:数据库连接池(Connection pooling)是程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对池中的连接进行申请,使用,释放.个人理解:创建数据库连接是一个很耗时的操作,也容易对数据库造成安全隐患.所以,在程序初始化的时候,集中创建多个数据库连接,并把他们集中管理,供程序使用,可以保证较快的数据库读写速度,还更加安全可靠. 二.为何要使用数据库连接池? 假设网站一天有很大的访问量,数据库服务器就需要为每次连接创建一次数据库连接,极