2025年druid数据库连接池是什么(druid连接池 mysql120s断开)

druid数据库连接池是什么(druid连接池 mysql120s断开)最近线上数据库迁到 haproxy 上 突然出现了很多数据库连接失败的错误 经过排查是因为我们使用了 Mysql 的 ReplicationD 数据库连接池使用的是 druid 而 druid 针对 Mysql Replication 连接的检查实现上有个 bug 导致的 当我正准备提 issue 的时候 发现很多人都遇到了这个问题 所以想写遍文章记录一下 话不多说

大家好,我是讯享网,很高兴认识大家。



最近线上数据库迁到haproxy上,突然出现了很多数据库连接失败的错误,经过排查是因为我们使用了Mysql的ReplicationDriver,数据库连接池使用的是druid, 而druid针对Mysql Replication 连接的检查实现上有个bug ,导致的。当我正准备提issue的时候,发现很多人都遇到了这个问题,所以想写遍文章记录一下。话不多说,下面我们来正式复盘一下这个问题和定位解决的过程。


讯享网

<p>之前公司的DBA新上线了一套HAProxy用来替代之前的VIP的高可用法案,所以我们也将从库的连接从VIP迁到了HAProxy.但是上线后不久我们发现线上开始出现数据库连接错误,而且凌晨的时候最多。</p> <pre></code></code></pre> <p>相信有一点经验的java开发对这个错误并不陌生,导致这个问题的原因也非常好定位,无非是客户端与数据库的连接被服务端主动断开,而客户端还傻傻的用这个已经被断开的连接去请求数据库,导致失败。</p> <p>我们知道Mysql有一个wait_timeout的配置会自动将空闲时间超过这个值(通常为8小时)的连接断开,因为物理数据库的配置并没有变动,而且代码中连接池中的配置的最小空闲时间远小于数据库wait_timeout,所以这个假设首先被排除。</p> <p>那么会不会是HAProxy将我们的连接给断开了呢(很有可能) ? 于是我们查看了HAProxy的配置</p> <pre></code></code></pre> <p>发现HAProxy会主动将空闲时间时间超过1分钟的连接断开,于是我们修改了druid的配置,将数据库空闲验证的时间修改为timeBetweenEvictionRunsMillis修改为50s(原来是60s,考虑到极限情况如果设置为60s的话依然会存在不能保活的情况),但是经过测试后我们发现问题依然存在。</p> <p>既然将保活时间设置到了60s以内为什么还会出现连接被断开呢?稍微思考一下,可能的原因不外乎只有两个</p> <ul> <li> <p>HAProxy维护的连接有问题</p> </li> <li> <p>保活策略没有生效</p> </li> </ul> <p>顺着这个思路我们首先排除了HAProxy的问题(简单来说就是HAProxy会保持客户端和服务器的会话,保证客户端到HAProxy的连接和HAProxy到服务器的连接是一致的,他们的空闲时间始终是一样的) 那么我们再来看看是不是保活策略没有生效呢?我们目前用的是druid来管理我们的数据库连接池 , 要弄清这个问题,我们得先看看druid是进行工作及怎么进行活性检测的。</p> <p><img data-src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10RCQHT68UmIsaYnESIAiagShuicDiaicXAeyxccRkzX1pkVnekd1p5KBjlfA/640?wx_fmt=png" data-type="png" src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10RCQHT68UmIsaYnESIAiagShuicDiaicXAeyxccRkzX1pkVnekd1p5KBjlfA/640?wx_fmt=png" loading="lazy"> 上面这张图表示了druid在获取线程池的大致的逻辑过程:druid在初始化时会创建两个守护线程,分别承担线程的创建和销毁任务,当用户线程出现等待获取线程的操作时(且线程池中的线程数不大于最大活动线程数),创建线程会自动创建新的连接并放到线程池中,所以当用户线程需要新的连接时,只需要直接从线程池获取即可。用户线程从线程池中获取到连接会根据用户的配置决定是否线程进行有效性验证,如果验证线程有效则返回线程,如果无效则将该连接关闭,(DestoryConnectionThread自动回收已关闭的连接),然后尝试重新从连接池中获取连接,知道获取到有效连接为止并返回连接。下面我们来看看代码的具体实现。 </p> <p><img data-src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10RsdVEOyia5jiaiaQLLbQ7HeDJXUWucnS24hbCAujmZZtzT1QjEHiadXiahRg/640?wx_fmt=png" data-type="png" src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10RsdVEOyia5jiaiaQLLbQ7HeDJXUWucnS24hbCAujmZZtzT1QjEHiadXiahRg/640?wx_fmt=png" loading="lazy"></p> <p><img data-src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10RQyqOGBaiaH6jsLOVB7icbTicWNRzyrONRnclhaE1EKOJkYh60tvSgEgZg/640?wx_fmt=png" data-type="png" src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10RQyqOGBaiaH6jsLOVB7icbTicWNRzyrONRnclhaE1EKOJkYh60tvSgEgZg/640?wx_fmt=png" loading="lazy"></p> <p>通过调试,我发现为false,也就是说这里的conn并不是,原来为了DB的读写分离项目使用的是数据库驱动是RepliationDriver而不是默认的Driver( ), 因此使用的连接也是, 而ReplicationConnection直接继承自  并没有继承 (仅限于mysql-connection-java 1.5.38版本之前,而我们线上使用的是1.5.35)</p> <blockquote> <p>  在mysql-connector-java 1.5.38版本之前(ReplicationConnection源码)的是 </p> </blockquote> <blockquote> <p>1.5.38([GitHub源码]开始将 抽象成接口(直接继承自 ),并使用他的的子类  (JDBC4ReplicationMySQLConnection源码 )来实现, 的内部功能则是由新增的代理类 来实现的 (ReplicationConnectionProxy实现了原本 类实现的功能)</p> </blockquote> <p>5.1.35版本</p> <p><img data-src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10RgvJ0cu3PkEn0JLGZ6hq9TdTghianuUTsL8Qd7bGoYsuF0maGsPjpGyQ/640?wx_fmt=png" data-type="png" src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10RgvJ0cu3PkEn0JLGZ6hq9TdTghianuUTsL8Qd7bGoYsuF0maGsPjpGyQ/640?wx_fmt=png" loading="lazy"></p> <p>5.1.47版本</p> <p><img data-src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10RGt6erxopdiaJDow69xRf0nvOHia3CB5tcZfcn1tGiaUCribNic1yQAt43Xw/640?wx_fmt=png" data-type="png" src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10RGt6erxopdiaJDow69xRf0nvOHia3CB5tcZfcn1tGiaUCribNic1yQAt43Xw/640?wx_fmt=png" loading="lazy"></p> <p><img data-src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10RHMMOszwMzPVErvAicctj6zN3ZuEWZdgaP3LlaNKYyQzNYkQic2WfjV2A/640?wx_fmt=png" data-type="png" src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10RHMMOszwMzPVErvAicctj6zN3ZuEWZdgaP3LlaNKYyQzNYkQic2WfjV2A/640?wx_fmt=png" loading="lazy"></p> <p>所以这里无论usePingMethod设置的值是什么,MySqlValidConnectionChecker都是执行SELECT 1 操作. 下面我们来具体看一下执行过程</p> <p>先打开Mysql的日志 (当然是本地开发环境喽,我这里为了方便,主从库配置的是同一个地址)</p> <pre></code></pre> <p>我们会得到以下日志(这里我只保留了核心日志信息,并进行了一些脱敏处理)</p> <p><img data-src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10RcfUnwzf0ibMiaLR6wv0dXIcT1BQutm5FNTfFohT3eibrwmnH9MIm5hRbA/640?wx_fmt=png" data-type="png" src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10RcfUnwzf0ibMiaLR6wv0dXIcT1BQutm5FNTfFohT3eibrwmnH9MIm5hRbA/640?wx_fmt=png" loading="lazy"></p> <p>我们很容易发现执行检查操作的线程和执行业务查询的线程是不一样的,因此我们可以断定检查用的连接和执行业务的数据库连接不是同一个,执行业务操作的数据库连接没有保活,空闲时间并没有被刷新,所以该连接一旦长时间没有访问就会被断开,导致出现连接不可用。</p> <p><img data-src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10RrK2iaAyruCa2wBTE8icPKJicMeZAMoyg7locuaAoAKhuqSib2AMKicEcQ/640?wx_fmt=png" data-type="png" src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10RrK2iaAyruCa2wBTE8icPKJicMeZAMoyg7locuaAoAKhuqSib2AMKicEcQ/640?wx_fmt=png" loading="lazy"></p> <p>问题找到了,解决方案也比较简单, druid的高版本已经支持自定义ValidConnectionChecker</p> <p><img data-src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10R6adsf3a4QLM4YSCwUwBia66VA3FsXrHcbDC0nKQv51ibqePHEss0E4Rg/640?wx_fmt=png" data-type="png" src="https://mmbiz.qpic.cn/mmbiz_png/CUesJ8d5QPyFibjbOrUXSDyickaKb0y10R6adsf3a4QLM4YSCwUwBia66VA3FsXrHcbDC0nKQv51ibqePHEss0E4Rg/640?wx_fmt=png" loading="lazy"></p> <blockquote> <p>1.5由于微信排版的限制,我将代码转成了图片,如果想看完整源码请访问我的博客blog.sunwaiting.com </p> </blockquote> 

讯享网
小讯
上一篇 2025-06-12 11:31
下一篇 2025-05-07 09:01

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/196190.html