客户端一段时间不访问,第一次访问数据库自动断开连接,刷新可以正常连接

本文讨论了数据库应用开发中遇到的数据库连接池断开问题,详细解释了问题原因,并提供了两种解决方法:定时检测数据库连接的有效性或修改数据库连接超时配置。同时,文章还介绍了异常处理逻辑,确保业务的重新处理。最后,强调了不推荐修改数据库服务器配置的原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

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

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

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

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值