起因
排查发现数据库连接数持续缓慢上涨,进而查看服务中连接数 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) 2. 解析找到相关的使用对象 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这个参数了?
原提问者GitHub用户huangjinteng
hyTimeoutMillis 是连接池中的一个参数,用于指定物理连接在多长时间内没有被使用就会被关闭。启用 phyTimeoutMillis 参数可以防止连接长时间占用而无法释放,从而提高连接池的性能和稳定性。但是,如果设置不当,可能会导致连接泄露的问题。
如果您在启用 phyTimeoutMillis 参数后出现了连接泄露的问题,可能是由于以下原因:
连接未正确关闭:如果应用程序没有正确释放连接,连接就会一直占用,导致连接泄露。建议在使用连接后,及时调用 close() 方法释放连接。
连接池配置不当:如果连接池的配置不当,可能会导致连接泄露。例如,连接池中的最大连接数设置过低,可能会导致连接不够用而无法释放。建议根据实际情况调整连接池的配置,以确保连接池的性能和稳定性。
数据库连接异常:如果数据库连接异常,可能会导致连接泄露。例如,数据库宕机或者网络不稳定等情况。建议在连接异常时,及时关闭连接,并进行相应的处理。
在连接池的设计中,尽量避免使用phyTimeoutMillis参数过长的设置。较长的phyTimeoutMillis值会导致连接池中的连接长时间得不到释放,进而增加连接的数量。
优化连接池泄露的解决方案一般包括以下几个方面:
及时归还连接:确保在使用连接之后,及时将连接归还到连接池中。这样可以避免连接泄露的情况。
定期检查连接的使用情况:通过监控和定期检查连接的使用情况,及时发现连接泄露的情况,并采取相应的措施进行处理。
使用连接池的销毁方法:在应用程序关闭或者不再需要连接池时,调用连接池的销毁方法,确保连接池中的连接得到正确的释放和清理。
对于数据库连接泄露的问题,你提供了一些排查和分析的结果,并提出了一些关于 Druid 连接池的优化建议。我将尝试回答你的问题和解释相关情况:
为什么没有优化这个 bug: 从你的描述中,我们可以看出你发现了 Druid 连接池中的一个可能导致连接泄露的问题,并提出了一些优化建议。然而,我作为 ChatGPT 并不是 Druid 的开发者,无法详细了解他们的决策过程。但是通常来说,开源项目的漏洞修复和功能改进都需要开发者评估其优先级、兼容性以及开发资源等因素。如果你认为这个问题十分重要且有广泛的影响,我建议你去 Druid 官方论坛或提交一个 GitHub Issue 来提醒开发者。
phyTimeoutMillis 参数: 你提到设置了 phyTimeoutMillis
参数,一般设置为小时级别。根据 Druid 文档,这个参数指定物理连接在连接池中最长的使用时间(单位为毫秒)。当连接超过该时间时,Druid 连接池会断开连接并将其标记为废弃状态,以便下次清理时被关闭和移除。然而,在连接被废弃后,它只有在 JVM 触发老年代的垃圾回收时才能释放,这可能导致连接泄露问题。
建议的优化: 你提到了一些关于 shrink
方法的优化建议,包括避免使用 Arrays.fill
操作并通过创建新的集合来保存需要保留的 DruidConnectionHolder
。这些优化建议看起来是为了减少空间占用和创建对象的开销。这些改进措施确实可以考虑,但最终是否被采纳取决于 Druid 开发团队对其优先级和可行性的评估。
需要注意的是,在进行任何优化之前,我们强烈建议你与 Druid 社区或开发者讨论该问题,并确保你理解了所有的影响和风险。他们可能会给出更准确和详细的解释,以及可能的解决方案或工作方式。
在使用 phyTimeoutMillis 参数后,可能导致连接池中的连接被占用,长时间无法及时释放,进而出现连接泄漏的情况。而 phyTimeoutMillis 参数的作用是设置物理连接在连接池中的最大存活时间,如果长时间没有活动,就会被连接池回收,以防止过期连接占用连接池资源。
针对此问题,Druid已经在版本1.2.9中进行了优化,在 ShrinkThread 中增加了一个判断:如果连接已经被标记为关闭状态,那么就无需进行回收操作,直接将该 DruidConnectionHolder 对象置为 null,让垃圾回收器回收该对象即可。
因此,建议将Druid更新到最新版本,以避免该问题。对于已经被回收的连接,如果没有被正确关闭,可能需要考虑优化应用程序代码,确保在使用完连接后及时关闭并归还到连接池中。 另外,建议以较短的时间来设置 phyTimeoutMillis 参数,以便及时释放连接,进而避免连接泄漏的风险。
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。