alibaba / druid

阿里云计算平台DataWorks(https://help.aliyun.com/document_detail/137663.html) 团队出品,为监控而生的数据库连接池
https://github.com/alibaba/druid/wiki
Apache License 2.0
27.9k stars 8.57k forks source link

连接池占用大量活跃连接,不释放。 #2594

Open jieyisoft opened 6 years ago

jieyisoft commented 6 years ago

环境

  1. druid版本:1.1.9,mybatis3.4.6+spring4.3.14
  2. DB环境:oracle12c RAC 2结点环境
  3. 应用环境:6个应用结点部署在6个独立虚拟化主机上,每个虚拟化主机配置都保持一致。应用结点的业务负责读5-6张表数据,没有事务,每张表数据量不大【1万-50万】

问题: 压力测试1000并发,通过F5负载至这6台应用主机,每个结点数据库初始连接50。发现6台机器,随机会有1台主机连接数会不断增长。查询druid监控界面的其他5台,活跃连接数都低于50,但出现问题的机器活跃连接数会逐渐增长到200(最大连接数)。后续将最大连接数放到600,连接数也一样吃完。查询代码都是通过mybatis mapper来进行调用,所以不存在手动打开连接但没关闭连接的问题,服务器压力比较大,负载在80%左右。 但将应用的druid连接池换成 Hikari3.1.0连接池,6台应用结点都稳定保持活跃连接50以下,没有出现上面问题。

比较奇怪的问题,@wenshao,望大神分析下。如下为druid配置。

druid 配置文件:

<property name="name" value="oracle-acq" />
<property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" />
<property name="initialSize" value="50" />
<property name="maxActive" value="200" />
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<property name="validationQuery" value="select 1 from dual" />
<property name="testOnBorrow" value="false" />
<property name="poolPreparedStatements" value="true" />
<property name="filters" value="stat,wall" />

wenshao commented 6 years ago

信息不足无法确定问题,这个关闭试试看

<property name="poolPreparedStatements" value="false" />
jieyisoft commented 6 years ago

<property name="poolPreparedStatements" value="false" /> <property name="maxPoolPreparedStatementPerConnectionSize" value="0" /> 修改成这样的、问题依旧。

然后跟踪那台有问题的JVM堆栈信息,发现50%的CPU消耗在DruidDataSource.pollLast上、20%消耗在DruidDataSource.recycle、20%消耗在DruidDataSource.getConnectionInternal 上。但其他5台堆栈里面这些完全没有。这台有问题的应用主机CPU比其他5台负载都高(负载均衡也做了调优,让这台有问题的机器接收到的请求也根据交易处理速度进行降低,大概降低了20%,但负载还是高),非常奇怪,@wenshao。

jieyisoft commented 6 years ago

由于业务都是查询,没有事务,每次查询都会从连接池获取连接并释放。 我们修改了业务使其开启事务,让一笔交易通过1个连接进行处理,减少连接获取和回收次数。发现负载能降低一点点,TPS 也能提高一点点。但本质还是某一台机器连接数不断增长。

jieyisoft commented 6 years ago

如下是完整的配置文件,MyDruidDataSource 重载了数据库密码加密功能,MySlf4jLogFilter 重载格式化了一些日志输出的SQL. <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"
` xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
default-autowire="byName">

<context:annotation-config /> 
<context:component-scan base-package="com" />

<bean id="propertyResources-db" class="java.util.ArrayList">  
    <constructor-arg>  
        <list>    
            <value>file:${YKT_ACQ_PATH}/jdbc.properties</value>
            <value>file:${YKT_ACQ_PATH}/redis.properties</value>
        </list>  
    </constructor-arg> 
</bean>
<!-- DRUID -->
<bean id="log-filter" class="com.ykt.druid.MySlf4jLogFilter">
    <property name="connectionLogEnabled" value="false" />
    <property name="statementPrepareAfterLogEnabled" value="false" />
    <property name="statementParameterClearLogEnable" value="false" />
</bean>

<bean id="dataSource-acq" class="com.ykt.druid.MyDruidDataSource"
    destroy-method="close" init-method="init">
    <!-- 基本属性 url、user、password -->  
    <property name="name" value="oracle-acq" />  
    <property name="url" value="${jdbc.url}" /> 
    <property name="driverClassName" value="${jdbc.driver}" /> 
    <property name="username" value="${jdbc.username}" />  
    <property name="password" value="${jdbc.password}" />  

    <!-- 配置初始化大小、最小、最大 -->  
    <property name="initialSize" value="50" />  
    <property name="minIdle" value="50" />   
    <property name="maxActive" value="100" />  

    <!-- 配置获取连接等待超时的时间 -->  
    <property name="maxWait" value="10000" />  

    <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->  
    <property name="timeBetweenEvictionRunsMillis" value="30000" />  

    <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->  
    <property name="minEvictableIdleTimeMillis" value="300000" />  

    <property name="validationQuery" value="select 1 from dual" />
    <property name="testWhileIdle" value="true" />  
    <property name="testOnBorrow" value="false" />  
    <property name="testOnReturn" value="false" />  

    <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->  
    <property name="poolPreparedStatements" value="true" />  
    <property name="maxPoolPreparedStatementPerConnectionSize" value="50" />  

    <!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 -->  
    <property name="filters" value="stat,wall" />   

    <!-- 保存DruidDataSource的监控记录 -->
    <property name="timeBetweenLogStatsMillis" value="300000" />

    <property name="proxyFilters">
        <list><ref bean="log-filter"/></list>
    </property>
</bean>

<bean id="SqlSessionFactory-acq" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource-acq" />
    <property name="configLocation" value="classpath:conf/mybatis/mybatis-config.xml" />
    <property name="mapperLocations" >
        <list>
            <value>classpath:conf/mybatis/mapper/ykt/**/*Mapper.xml/</value>
            <value>classpath:conf/mybatis/mapper/acq/**/*Mapper.xml/</value>
        </list>
    </property>
</bean>

<bean id="txManager-acq" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource-acq" />
</bean>

<tx:annotation-driven transaction-manager="txManager-acq" />

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.acq.dal.dao,com.ykt.dal.dao"/>  
    <property name="sqlSessionFactoryBeanName" value="SqlSessionFactory-acq" />
</bean>

<!-- 加载redis缓存相关服务bean -->
<bean id="redisOprService" class="com.ykt.dal.redis.RedisOprService" />
<bean id="redisQueryService" class="com.ykt.dal.redis.RedisQueryService" />
<bean id="redisDalService" class="com.ykt.dal.redis.RedisDalService" />

`

Grrui commented 5 years ago

@jieyisoft 问题解决了吗?我也遇到类似的问题了。

fengyuelove commented 5 years ago

同样问题

taolive commented 5 years ago

同样问题,尤其适用springboot后,都不会手动去关闭connection的,怎么破?

Johnson-Jia commented 5 years ago

同样问题,使用springboot 连接不释放

haochencheng commented 4 years ago

同样问题 100 个连接 一会就占满了 只有 某个 sql 是这样 。

kaisesai commented 4 years ago

一样的问题,我使用了 1.1.21 版本出现了短暂的 cpu 彪高连接数激增,后来换成 1.0.18 版本后没有出现过这问题

xuyixun21 commented 4 years ago

一样的问题,我使用了 1.1.21 版本出现了短暂的 cpu 彪高连接数激增,后来换成 1.0.18 版本后没有出现过这问题

是druid版本问题?你用的是druid-spring-boot-starter么?

kaisesai commented 4 years ago

一样的问题,我使用了 1.1.21 版本出现了短暂的 cpu 彪高连接数激增,后来换成 1.0.18 版本后没有出现过这问题

是druid版本问题?你用的是druid-spring-boot-starter么?

没有用 druid-spring-boot-starter,直接引的包

Leon02109 commented 4 years ago

同样的问题,我用的版本是1.1.9,想问一下有解决的吗?

fahgrjgdjfle commented 3 years ago

同样问题,高并发下线程大量时间阻塞在pollLast上

sai129198 commented 3 years ago

版本1.2.6,引的druid的spring-boot-starter同样遇到这个问题。。。

Jenfong commented 2 years ago

druid版本1.1.9,同样中招 其他环境信息:

依赖:

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.9</version>
        </dependency>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.9</version>

配置:

spring:
  datasource:
    druid:
      XXXXXX:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: ***
        username: ***
        password: ***
        initial-size: 1
        min-idle: 1
        max-active: 20
        max-wait: 60000
        time-between-eviction-runs-millis: 60000
        min-evictable-idle-time-millis: 300000
        validation-query: SELECT 'x'
        test-while-idle: true
        test-on-borrow: false
        test-on-return: false
        pool-prepared-statements: false
        max-pool-prepared-statement-per-connection-size: 20
907474646 commented 2 years ago

Did you hava a solution for this question?

lixiaofengCH commented 2 years ago

druid1.2.4 mysql活跃连接数一直增加,不释放;oracle等待一些时间会释放;这是什么问题呢?

lixiaofengCH commented 2 years ago

druid1.2.4 mysql活跃连接数一直增加,不释放;oracle等待一些时间会释放;这是什么问题呢?

在释放连接conn.closed()之后,加上dataSource.removeAbandoned()成功解决mysql活跃连接数一直激增的问题!

lixiaofengCH commented 2 years ago

druid1.2.4 mysql活跃连接数一直增加,不释放;oracle等待一些时间会释放;这是什么问题呢?

STEP-1:在释放连接conn.closed()之后,加上dataSource.removeAbandoned()成功解决mysql活跃连接数一直激增的问题!

问题核心:连接泄露; STEP-2:在STEP-1之后,开启dataSource.setLogAbandoned(true),用于扫描回收的泄露连接;根据打印的日志追踪到泄露连接处的代码,然后释放连接;问题就解决了!多线程测试时active连接数最大为3,连接池活跃连接数释放的问题解决!

Endeavour86 commented 2 years ago

遇到同样的问题 很无助

mengpeiwei commented 1 year ago

1.2.16也出现这种情况,并发下连接一直涨,设置了清除时间也不行

mengpeiwei commented 1 year ago

不过加了以下配置好像可以回收了,再看看吧

超过时间限制是否回收

        removeAbandoned: true
        # 超时时间;单位为秒。180秒=3分钟
        removeAbandonedTimeout: 300
        # 关闭abanded连接时输出错误日志----定位问题的时候设为true,生产的时候设为false
        logAbandoned: false
Rorchal commented 9 months ago

@mengpeiwei 你的情况是咋复现的啊