Apache Druid连接回收引发的血案

news2024/11/24 1:03:49

问题

线上执行大批量定时任务,发现SQL执行失败的报错:

CommunicationsException, druid version 1.1.10, 
jdbcUrl : jdbc:mysql://xxx?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull,
 testWhileIdle true, idle millis 658108, minIdle 2, poolingCount 1,
  timeBetweenEvictionRunsMillis 60000, lastValidIdleMillis 658108, 
  driver com.mysql.jdbc.Driver, exceptionSorter com.alibaba.druid.pool.vendor.MySqlExceptionSorter
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet successfully received from the server was 658,107 milliseconds ago.  The last packet sent successfully to the server was 0 milliseconds ago.
	at sun.reflect.GeneratedConstructorAccessor60.newInstance(Unknown Source)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
	at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:981)
	at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3465)
	at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3365)
	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3805)
	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2478)
	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2625)
	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2551)
	at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1861)
	at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1962)
	at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_executeQuery(FilterChainImpl.java:3188)
	at com.alibaba.druid.filter.FilterEventAdapter.preparedStatement_executeQuery(FilterEventAdapter.java:465)
	at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_executeQuery(FilterChainImpl.java:3185)
	at com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl.executeQuery(PreparedStatementProxyImpl.java:181)
	at com.alibaba.druid.pool.DruidPooledPreparedStatement.executeQuery(DruidPooledPreparedStatement.java:228)
	at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:57)
	at org.hibernate.loader.Loader.getResultSet(Loader.java:2304)

追查

从错误日志看来,是连接MySQL超时,但是这个超时时间感觉有点离谱,查看了一下执行的SQL,是非常简单的SQL,理论上不可能造成超时。
联系DBA,看看MySQL的日志能否发现什么蛛丝马迹。

原因

经过DBA排查,确认是MySQL的空闲连接清理参数配置,MySQL的interactive_timeout参数指定了连接空闲多久后会被回收掉,缺省配置为600秒。
在这里插入图片描述

问题初步定位,是因为数据库连接池Druid持有的数据库连接,已经被MySQL回收,此时有SQL执行时,已经被回收掉的连接再此被分配,就会导致这个报错。
那么问题来了,为什么数据库连接池没有感知到这个连接已经被回收了呢?

连接池是怎么判断一条连接是Idle状态的?

带着疑问,翻找Druid文档,发现两个配置:

  • minEvictableIdleTimeMillis:最小空闲时间,默认30分钟,如果连接池中非运行中的连接数大于minIdle,并且那部分连接的非运行时间大于minEvictableIdleTimeMillis,则连接池会将那部分连接设置成Idle状态并关闭;也就是说如果一条连接30分钟都没有使用到,并且这种连接的数量超过了minIdle,则这些连接就会被关闭了。

  • maxEvictableIdleTimeMillis:最大空闲时间,默认7小时,如果minIdle设置得比较大,连接池中的空闲连接数一直没有超过minIdle,这时那些空闲连接是不是一直不用关闭?当然不是,如果连接太久没用,数据库也会把它关闭,这时如果连接池不把这条连接关闭,系统就会拿到一条已经被数据库关闭的连接。为了避免这种情况,Druid会判断池中的连接如果非运行时间大于maxEvictableIdleTimeMillis,也会强行把它关闭,而不用判断空闲连接数是否小于minIdle。

原来Druid是根据这两个配置来确认什么时候回收空闲的连接,为了验证是否如此,看一下源码中如何使用这两个配置。

我们使用的Druid版本是1.1.10。

com.alibaba.druid.pool.DruidDataSource

    public class DestroyTask implements Runnable {

        @Override
        public void run() {
            shrink(true, keepAlive);

            if (isRemoveAbandoned()) {
                removeAbandoned();
            }
        }

    }

追踪源码,定位到DruidDataSource,*DestroyTask shrink()*负责释放连接:

public void shrink(boolean checkTime, boolean keepAlive) {
        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            return;
        }

        int evictCount = 0;
        int keepAliveCount = 0;
        try {
            if (!inited) {
                return;
            }

            final int checkCount = poolingCount - minIdle;
            final long currentTimeMillis = System.currentTimeMillis();
            for (int i = 0; i < poolingCount; ++i) {
                DruidConnectionHolder connection = connections[i];

                if (checkTime) {
                    if (phyTimeoutMillis > 0) {
                        long phyConnectTimeMillis = currentTimeMillis - connection.connectTimeMillis;
                        if (phyConnectTimeMillis > phyTimeoutMillis) {
                            evictConnections[evictCount++] = connection;
                            continue;
                        }
                    }

                    long idleMillis = currentTimeMillis - connection.lastActiveTimeMillis;

                    if (idleMillis < minEvictableIdleTimeMillis) {
                        break;
                    }

                    if (checkTime && i < checkCount) {
                        evictConnections[evictCount++] = connection;
                    } else if (idleMillis > maxEvictableIdleTimeMillis) {
                        evictConnections[evictCount++] = connection;
                    } else if (keepAlive) {
                        keepAliveConnections[keepAliveCount++] = connection;
                    }
                } else {
                    if (i < checkCount) {
                        evictConnections[evictCount++] = connection;
                    } else {
                        break;
                    }
                }
            }
			.........
			
    }

在*shrink()*方法中我们找到了minEvictableIdleTimeMillis和maxEvictableIdleTimeMillis,等等,好像哪里不对?

		if (idleMillis < minEvictableIdleTimeMillis) {
          	break;
         }

         if (checkTime && i < checkCount) {
             evictConnections[evictCount++] = connection;
         } else if (idleMillis > maxEvictableIdleTimeMillis) {
             evictConnections[evictCount++] = connection;
         } else if (keepAlive) {
             keepAliveConnections[keepAliveCount++] = connection;
         }

这里与文档的解释好像不一样?当连接空闲时间小于minEvictableIdleTimeMillis,退出循环检测,如果大于minEvictableIdleTimeMillis,判断空闲时间大于maxEvictableIdleTimeMillis,将连接加入需要释放的数组中。

那也就是说,如果按照缺省配置,minEvictableIdleTimeMillis 30分钟,maxEvictableIdleTimeMillis 7天,确实可能会出现Druid认为连接还存活着,但MySQL判断空闲时间超过配置,将会回收连接。

配置建议

maxEvictableIdleTimeMillis 要小于等于 MySQL Server端interactive_timeout 配置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1189585.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《向经典致敬》第二届粤港澳大湾区著名歌唱家音乐会完美落幕

百年经典 歌坛盛会 “《向经典致敬》第二届粤港澳大湾区著名歌唱家音乐会暨2023福田人才之夜”完美落幕 2023年11月4日&#xff0c;阳光普照&#xff0c;秋意正浓&#xff0c;由中共深圳市福田区委宣传部、深圳市福田区文学艺术界联合会主办&#xff0c;深圳歌唱家协会承办&…

数据结构与算法C语言版学习笔记(3)-线性表的链式结构:链表

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言&#xff1a;回顾顺序表的优缺点&#xff1a;为什么要引入链式结构的线性表&#xff1f; 一、什么是链表&#xff1f;二、链表的分类①为什么要设置头节点&…

Oracle(15)Managing Users

目录 一、基础知识 1、Users and Security 用户和安全 2、Database Schema 3、Checklist for Creating Users创建用户步骤 二、基础操作 1、创建一个用户 2、OS Authentication 操作系统身份验证 3、Dropping a User 删除用户 4、Getting User Information 获取用户信…

Idea安装使用教程~

在本文中&#xff0c;我们将提供关于如何安装 IntelliJ IDEA 的详细步骤。如果您是初学者或只是想尝试一下 IDEA&#xff0c;我们建议您下载 Community 版。如果您需要更多高级功能&#xff0c;可以选择 Ultimate 版。 步骤一&#xff1a;下载 IntelliJ IDEA 首先&#xff0c;…

第三方商城对接项目(202311)

文章目录 1. 项目背景和目标2. 项目成果3. 项目经验总结4. 展望和建议 1. 项目背景和目标 竞标成功接口对接第三方商城&#xff0c;商品&#xff0c;订单&#xff0c;售后尽快完成对接 2. 项目成果 完成整个项目功能流程对接新业务功能移交项目等业务部门使用 3. 项目经验总…

app自动化测试——capability 配置参数解析

一、Capability 简介 功能&#xff1a;配置 Appium 会话&#xff0c;告诉 Appium 服务器需要自动化的平台的应用程序 形式&#xff1a;键值对的集合&#xff0c;键对应设置的名称&#xff0c;值对应设置的值 主要分为三部分 公共部分 ios 部分 android 部分 二、Session Appi…

【C++】特殊类实现——设计一个类、不能被拷贝、只能在堆上创建对象、只能在栈上创建对象、不能被继承、单例模式、饿汉模式、懒汉模式

文章目录 C特殊类实现1.设计一个类、不能被拷贝2.设计一个类、只能在堆上创建对象3.设计一个类、只能在栈上创建对象4.设计一个类、不能被继承5.设计一个类&#xff0c;只能创建一个对象(单例模式)5.1饿汉模式5.2懒汉模式 C 特殊类实现 1.设计一个类、不能被拷贝 在C中&#x…

11 # 手写 reduce 方法

reduce 使用 reduce() 方法对数组中的每个元素按序执行一个提供的 reducer 函数&#xff0c;每一次运行 reducer 会将先前元素的计算结果作为参数传入&#xff0c;最后将其结果汇总为单个返回值。 第一次执行回调函数时&#xff0c;不存在“上一次的计算结果”。如果需要回调…

短短45分钟,Open AI撼动了整个AI圈?

相信关注AI行业的人没有人不知道ChatGPT&#xff0c;作为人工智能新产品&#xff0c;ChatGPT一经发出就引爆全球&#xff0c;也让一众企业走上了探索AI大模型之路。而就在国内一众企业就AI大模型不断改进创新时&#xff0c;Open AI用一场仅45分钟的发布会&#xff0c;震惊了整个…

【JavaEESpring】Spring IoCDI

Spring IoC& DI 1. IoC2. IoC & DI 使⽤2.1 Bean的存储2.1 DI 注入 Autowired 3. 练习代码自取 1. IoC Spring 是包含了众多⼯具⽅法的 IoC 容器 IoC: Inversion of Control (控制反转), 也就是说 Spring 是⼀个"控制反转"的容器。 什么是控制反转呢? 也就…

uniapp使用vue

uniapp集成了Vuex&#xff0c;&#xff0c;并不需要安装vuex 定义自己的vuex vuex中独立命名空间&#xff1a; 可以在模块中使用 namespaced 属性&#xff0c;设置为 true&#xff0c;&#xff0c;这样做的好处是&#xff0c;&#xff0c;不同模块之间的state&#xff0c;mut…

电商库存随笔

好多年没有来写东西了&#xff0c;忙成狗&#xff0c;最近闲暇&#xff0c;有点时间&#xff0c;随手写一下之前的项目中的小点&#xff1b; 一方面是做个总结&#xff0c;一方面打发一下时间 出库 库存扣减时机 下单扣减 [生成订单]付款扣减预扣库存(实际使用) 预扣库存 并…

运营商大数据精准获客:我们提供精准客源渠道的最大资源体?

运营商大数据精准营销 谈起精准获客&#xff0c;竞争对手永远是为我们提供精准客源渠道的最大资源体&#xff01; 最新的获客方式&#xff0c;就是从竞争对手的手中把他们的精准客户资源变为自己的。 今年最火的运营商大数据精准营销是拒绝传统营销方式的烧钱推广&#xff0…

Nginx缓存基础

1 nginx缓存的流程 客户端需要访问服务器的数据时&#xff0c;如果都直接向服务器发送请求&#xff0c;服务器接收过多的请求&#xff0c;压力会比较大&#xff0c;也比较耗时&#xff1b;而如果在nginx缓存一定的数据&#xff0c;使客户端向基于nginx的代理服务器发送请求&…

ChatGPT - 在ChatGPT中设置通用提示模板

文章目录 Prompt设置验证 Prompt VERBOSITY: 我可能会使用 V[0-3] 来定义代码的详细程度&#xff1a;V0 简洁明了 V1 简练 V2 详细 V3 非常详细&#xff0c;附有例子助理回应 您是用户问题背景下的主题专家。我们一步一步来&#xff1a;除非您只是回答一个简短的问题&#xff…

Confluence 漏洞复现(CVE-2023-22515)

Confluence 漏洞复现&#xff08;CVE-2023-22515&#xff0c;CVE-2023-22518&#xff09; 1.CVE-2023-22515权限提升漏洞 1.1漏洞描述 Confluence近期推出的严重漏洞cve-2023-22515&#xff0c;由于未授权和xwork框架问题&#xff0c;导致攻击者可以未授权将系统设置为未安装…

分布式事务的华丽进化

说到分布式事务&#xff0c;大家并不陌生。之前我已做过相关的总结&#xff08;连接附本文后面&#xff09;&#xff0c;不过比较偏理论。在实际工作中&#xff0c;用得比较多的还是柔性分布式事务&#xff0c;今天主要把在工作中运用到的几种柔性分布式事务的场景及实现方式做…

使用nginx作为图片服务器

第一步&#xff1a; 下载nginx版本&#xff0c;去官网下载&#xff0c;这里不建议下载最新版本&#xff0c;因为有时候最新版本还不太稳定。 nginx下载地址官网&#xff1a;nginx: download&#xff0c;然后把下载好的安装包解压出来。 第二步&#xff1a; 在nginx目录下载创建…

在新的服务器上成功安装mysqlclient的方法【解决No matching distribution found for mysqlclient的问题】

前言&#xff1a;在某台Centos服务器上安装mysqlclient时一直报下面的错&#xff1a; WARNING: Discarding https://mirrors.aliyun.com/pypi/packages/6a/91/bdfe808fb5dc99a5f65833b370818161b77ef6d1e19b488e4c146ab615aa/mysqlclient-1.3.0.tar.gz#sha25606eb5664e3738b28…

【2】Spring Boot 3 项目搭建

目录 【2】Spring Boot 3 初始项目搭建项目生成1. 使用IDEA商业版创建2. 使用官方start脚手架创建 配置与启动Git版本控制 个人主页: 【⭐️个人主页】 需要您的【&#x1f496; 点赞关注】支持 &#x1f4af; 【2】Spring Boot 3 初始项目搭建 项目生成 1. 使用IDEA商业版创…