[alibaba/druid]连接池启用phyTimeoutMillis之后造成连接泄露

2025-11-10 1 views
5

起因

排查发现数据库连接数持续缓慢上涨,进而查看服务中连接数 lsof -Pni | grep mysql ip port 为142条记录,超出maxActive=100的大小 
druid版本1.2.8 配置的其他参数除了url user pwd只有如下几条:
driver=mysql
phyTimeoutMillis = 1小时
minIdle  = 5
maxActive = 100

排查 从端口号反查出相应关联的druid维护的连接,排查连接是如何泄露的 1.先找到被泄露端口,把服务无流量2个小时后执行查询端口命令("链接最长使用时间"为1小时,按理不应该还能找到连接) lsof -Pni |grep "xx:3306" java 533 xxx 254u IPv6 xxx 0t0 TCP xx:49682->xx:3306 (ESTABLISHED)

解析找到相关的使用对象 jmap -dump文件,visualvm工具从socket端口号找到相应关联的DruidConnectionHolder对象,下边是排查过程。 使用visualvm 的OQl查询: select s from java.net.SocksSocketImpl s where s.localport == 49682 java.net.SocksSocketImpl(localport=49682) -> MysqlIO -> JDBC4Connection -> druidConnectionHolder(无任何对象引用) 根据查看druidConnectionHolder对象得到以下3条信息,并未过期,并未被关闭,并未正在使用中。

接下来分析DruidConnectionHolder是如何泄露 连接池DruidConnectionHolder[] connections 的修改有如下几个方法: 取出连接polllast、归还连接putLast、定时清理连接shrink。 在shrink方法主要是作链接的保活、丢弃清理, 方法中有个操作对connections数组进行操作 -> System.arraycopy Arrays.fill 这个操作可能会把connections里正常的部分链接置空,如果被置空的DruidConnectionHolder如果还处于年轻代则会很快被finalize清理,而进入了老年代则必须等到"full gc"链接才能回收,就导致了线上服务如果没触发full gc则链接一直保存变成缓慢增长。

尝试解决 shrink 时候为何新建集合保存真正需要留存下来的DruidConnectionHolder,剔除了原有的Arrays.fill逻辑,最后将留存的集合toArray返回给connections,而keepAliveConnections evictConnections 则可以维持原逻辑。

想问下druid的大佬为啥不优化这个bug? DestroyConnectionThread对链接的保活、丢弃后只任意保留一部分,以及shrink操作尽量节省空间少创建集合对象,但是holder泄露会导致socket连接增加且没上限,而设置phyTimeoutMillis一般都是小时级别,被丢弃的DruidConnectionHolder都已经熬到了老年代,只有jvm被触发了老年代清理才有可能释放相应被泄露的链接。或者是隐晦的表明不要继续使用phyTimeoutMillis这个参数了?

回答

5

1.2.9优化过这个问题。

0

多谢大神回复,学习一下下个版本。

9

1.2.9优化过这个问题。

貌似没有1.2.9的tag,目前master分支也并没有修复连接泄露的问题。