一则线上MySql连接异常的排查过程
Mysql作为一个常用数据库,在互联网系统应用很多。有些故障是其自身的bug,有些则不是,这里以前段时间遇到的问题举例。
问题
当时遇到的症状是这样的,我们的应用在线上测试环境,JMeter测试过程中,发现每次压力测试开始时访问低前几个http request请求会超时,而之后的请求持续测试中都不会。最后一点是Tomcat的log并没有报什么错误。
压测的内容就是起200线程不停的向这个http页面发送请求,这个页面逻辑也比较简单,会在后端向数据库插入一条数据,连接池采用阿里的Druid(这个坑先留在这),tomcat中运行的是常规web app应用,每个应用的JDBC连接池最大连接数只设了30,就是说就算4个tomcat一起连数据库,最大也没有多少连接。
尝试排查
由于tomcat的log并没有什么错,所以先开始尝试重现错误。 错误重现开始并不容易,因为看起来比较随机,后来经过总结,发现每次都是出现问题都是应用放了一晚上后,测试人员早上过来开始压力测试时出现,开始怀疑跟闲置有关,所以后面的重现都按这个方式来,闲置半小时再开始尝试重现。
找log
没有log,就要看下JVM的stack信息了。重现故障,上该机器上用jstack直接抓问题tomcat 的jvm信息。
jps
列出机器的java进程号
jstack javaid
dump该java进程的stack信息
拿到的stack信息中发现了有用的东西:
"http-bio-8081-exec-4975" daemon prio=10 tid=0x00007f9d4c127000 nid=0x65db runnable [0x00007f9cc4544000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:114)
at com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:161)
at com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:189)
- locked <0x0000000684d608c8> (a com.mysql.jdbc.util.ReadAheadInputStream)
at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3014)
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3467)
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3456)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3997)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2468)
at com.mysql.jdbc.ConnectionImpl.pingInternal(ConnectionImpl.java:4092)
at com.mysql.jdbc.ConnectionImpl.ping(ConnectionImpl.java:4069)
at sun.reflect.GeneratedMethodAccessor94.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.alibaba.druid.pool.vendor.MySqlValidConnectionChecker.isValidConnection(MySqlValidConnectionChecker.java:98)
at com.alibaba.druid.pool.DruidAbstractDataSource.testConnectionInternal(DruidAbstractDataSource.java:1235)
at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:928)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:882)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:872)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:97)
at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:202)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:372)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:417)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:255)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy27.insert(Unknown Source)
可以看到HTTP请求从前端容器直到数据库读取时为止,卡在了数据库读取的地方,而且并不是JDBC驱动代码里的问题,而且出在socket读取的地方:
com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:189)
- locked <0x0000000684d608c8> (a com.mysql.jdbc.util.ReadAheadInputStream)*
根据这个错误搜索了下,唯一有价值的就是N年前mysql官网上报的一个bug,同样的错误,但处理的办法并不治本。如提到了将JDBC连接字串改为:
useReadAheadInput=false&useUnbufferedInput=false
这只是让socket不去预读网络缓冲区,但实际上这个时候Mysql的连接已经断开了,并不知道是web应用这里断的还是Mysql断的。
查找连接池超时
由于根据log看起来是应用的客户端在socket上读不到东西,肯定是应用与mysql的tcp连接断了,所以开始排查应用连接池设置与mysql的连接超时设置。
应用连接池设置
name="maxWait" value="60000"
获取连接最大等待60秒
name="testWhileIdle" value="true"
测试空闲连接
name="minEvictableIdleTimeMillis" value="300000"
name="timeBetweenEvictionRunsMillis" value="60000"
Destroy线程会检测连接的间隔时间
应用端的连接池设置没有主动断掉的设置。
mysql连接超时设置
show global variables like '%timeout%'
看到mysql维持连接的timeout时间为28800,即8小时,数据库端不会断掉这个连接。
至此,问题的排查进入死胡同,两边都不会主动断开连接,为什么客户端在闲置几分钟后会被断掉?
还有一个疑点是同样的代码,数据库也没什么变动,在另一个纯测试环境完全没有这个问题。
查找网络问题
现在问题的重点怀疑方向就是线上环境网络问题。于是找运维的同事查看了下数据库机器上linux有没有什么异常的配置,结果是没有。
期间也怀疑为什么用了阿里druid的连接池,现在设成每分钟检测连接池里的连接,还是会在拿到连接的时候有失效的连接。
解决
断断续续折腾了2天,抱着死马当活马医去咨询了其他部门的同事,结果那兄弟说是不是闲置后卡在socketRead上?然后问了应用与数据库是不是在不同网段上,马上建议找网络的人查一下防火墙对tcp长连接超时的设置。
这时候基本上就肯定是防火墙设置的问题,排查后发现两个网段间华为交换机的长连接超时设了3分钟,由于java应用的连接池是尽量长时间的维持连接(几个小时,低于数据库的最长8小时设置),而防火墙认为超过3分钟的连接是有问题的,直接断掉了,这时应用与mysql都不知道tcp连接已经被断了。
此次故障还暴露了阿里Druid开源连接池对连接处理逻辑的问题,连接池并没有用单独的线程去检测所有连接有没有断开,查代码后发现其只是在拿到连接时测试连接是否有效,处理逻辑没有老牌c3p0严谨,之后将应用连接池实现更换为c3p0。
文章来自微信平台「麦芽面包」。转载请注明。
一则线上MySql连接异常的排查过程的更多相关文章
- 原创 记录一次线上Mysql慢查询问题排查过程
背景 前段时间收到运维反馈,线上Mysql数据库凌晨时候出现慢查询的报警,并把原始sql发了过来: --去除了业务含义的sql update test_user set a=1 where id=1; ...
- Linux(2)---记录一次线上服务 CPU 100%的排查过程
Linux(2)---记录一次线上服务 CPU 100%的排查过程 当时产生CPU飙升接近100%的原因是因为项目中的websocket时时断开又重连导致CPU飙升接近100% .如何排查的呢 是通过 ...
- 记一次排查线上MySQL死锁过程,不能只会curd,还要知道加锁原理
昨晚我正在床上睡得着着的,突然来了一条短信. 啥,线上MySQL死锁了,我赶紧登录线上系统,查看业务日志. 能清楚看到是这条insert语句发生了死锁. MySQL如果检测到两个事务发生了死锁,会回滚 ...
- 一次线上CPU高的问题排查实践
一次线上CPU高的问题排查实践 前言 近期某一天上班一开电脑,就收到了运维警报,有两台服务CPU负载很高,同时收到一线同事反馈 系统访问速度非常慢,几乎无响应. 一个美好的早晨,最怕什么就来什么.只好 ...
- 线上Mysql数据库崩溃事故的原因和处理
前文提要 承接前文<一次线上Mysql数据库崩溃事故的记录>,在文章中讲到了一次线上数据库崩溃的事件记录,建议两篇文章结合在一起看,不至于摸不着头脑. 由于时间原因,其中只讲了当时的一些经 ...
- Spring+SpringMVC+MyBatis+easyUI整合进阶篇(八)线上Mysql数据库崩溃事故的原因和处理
前文提要 承接前文<一次线上Mysql数据库崩溃事故的记录>,在文章中讲到了一次线上数据库崩溃的事件记录,建议两篇文章结合在一起看,不至于摸不着头脑. 由于时间原因,其中只讲了当时的一些经 ...
- 线上CPU飙升100%问题排查
本文转载自线上CPU飙升100%问题排查 引子 对于互联网公司,线上CPU飙升的问题很常见(例如某个活动开始,流量突然飙升时),按照本文的步骤排查,基本1分钟即可搞定!特此整理排查方法一篇,供大家参考 ...
- 探针配置失误,线上容器应用异常死锁后,kubernetes集群未及时响应自愈重启容器?
探针配置失误,线上容器应用异常死锁后,kubernetes集群未及时响应自愈重启容器? 探针配置失误,线上容器应用异常死锁后,kubernetes集群未及时响应自愈重启容器? 线上多个服务应用陷入了死 ...
- 记一次线上服务CPU 100%的处理过程
告警 正在开会,突然钉钉告警声响个不停,同时市场人员反馈客户在投诉系统登不进了,报504错误.查看钉钉上的告警信息,几台业务服务器节点全部报CPU超过告警阈值,达100%. 赶紧从会上下来,SSH登录 ...
随机推荐
- 移动web前端小结(一)--摘自小鹿_同学
一.框架 框架:Bootstrap+HTML5 Boilerplate. 两个框架整合到一起可以看一下这位大神的文章:<使用 Bootstrap 和 HTML5 Boilerplate 开始一个 ...
- NetSuite API - SuiteScript API.js
/* from: http://blog.csdn.net/levin_rain/article/details/8571444 */ ' Core SuiteScript Functions ' f ...
- 断言(ASSERT)的用法
ASSERT ()是一个调试程序时经常使用的宏,在程序运行时它计算括号内的表达式,如果表达式为FALSE (0), 程序将报告错误,并终止执行.如果表达式不为0,则继续执行后面的语句.这个宏通常原来判 ...
- 【洛谷P3385】模板-负环
这道题普通的bfs spfa或者ballen ford会T 所以我们使用dfs spfa 原因在于,bfs sfpa中每个节点的入队次数不定,退出操作不及时,而dfs则不会 既然,我们需要找负环,那么 ...
- Java程序员从笨鸟到菜鸟之(一百零一)sql注入攻击详解(二)sql注入过程详解
在上篇博客中我们分析了sql注入的原理,今天我们就来看一下sql注入的整体过程,也就是说如何进行sql注入,由于本人数据库和网络方面知识有限,此文章是对网上大量同类文章的分析与总结,其中有不少直接引用 ...
- ELK 5.0 组件后台启动
elasticsearch 后台启动,只需要 在bin目录下执行: ./elasticsearch -d 查看是否启动成功使用: ps aux|grep elasticsearch kibana 后台 ...
- java 单利模式
首先何为单利模式: 单利模式即多次调用同一个对象的时候,只有一个实例(这里所谓的实例就是,假如创建了两个对象,它们的hashCode相同) 下面是相关代码: 1 创建一个对象Singleton类 pa ...
- 关于ubuntu16.4 中安装最新的eclipse或者是STS出现页面特卡,且新建项目没有提示,preference选项中点击左侧标签右侧没反应的解决办法,参照google, 排版不太好,希望对一些小伙伴有所帮助
up vote21down votefavorite 12 Eclipse was working as good as anything on 14.04. I did a clean instal ...
- Linux 常用工具小结:(5) lftp工具使用
Linux 常用工具小结:(1) lftp工具使用. 这里会按照一些比较常用的功能列出,并举一个具体的例子逐一解释功能. 通常使用ftp过程是登陆ftp,浏览ftp内容,下载ftp文件,或者上传ftp ...
- ProceedingJoinPoint获取当前方法
aspectJ切面通过ProceedingJoinPoint想要获取当前执行的方法: 错误方法: Signature s = pjp.getSignature(); MethodSignatu ...