一则线上MySql连接异常的排查过程

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: Mysql作为一个常用数据库,在互联网系统应用很多。有些故障是其自身的bug,有些则不是,这里以前段时间遇到的问题举例。

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。


文章来自微信平台「麦芽面包」。转载请注明。

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
2月前
|
关系型数据库 MySQL 数据库连接
数据库连接工具连接mysql提示:“Host ‘172.23.0.1‘ is not allowed to connect to this MySQL server“
docker-compose部署mysql8服务后,连接时提示不允许连接问题解决
|
27天前
|
关系型数据库 MySQL 网络安全
如何排查和解决PHP连接数据库MYSQL失败写锁的问题
通过本文的介绍,您可以系统地了解如何排查和解决PHP连接MySQL数据库失败及写锁问题。通过检查配置、确保服务启动、调整防火墙设置和用户权限,以及识别和解决长时间运行的事务和死锁问题,可以有效地保障应用的稳定运行。
131 25
|
1月前
|
关系型数据库 MySQL 数据库连接
Unity连接Mysql数据库 增 删 改 查
在 Unity 中连接 MySQL 数据库,需使用 MySQL Connector/NET 作为数据库连接驱动,通过提供服务器地址、端口、用户名和密码等信息建立 TCP/IP 连接。代码示例展示了如何创建连接对象并执行增删改查操作,确保数据交互的实现。测试代码中,通过 `MySqlConnection` 类连接数据库,并使用 `MySqlCommand` 执行 SQL 语句,实现数据的查询、插入、删除和更新功能。
|
6月前
|
SQL 关系型数据库 MySQL
MySQL如何排查和删除重复数据
该文章介绍了在MySQL中如何排查和删除重复数据的方法,包括通过组合字段生成唯一标识符以及使用子查询和聚合函数来定位并删除重复记录的具体步骤。
549 2
|
4月前
|
关系型数据库 MySQL 网络安全
DBeaver连接MySQL提示Access denied for user ‘‘@‘ip‘ (using password: YES)
“Access denied for user ''@'ip' (using password: YES)”错误通常与MySQL用户权限配置或网络设置有关。通过检查并正确配置用户名和密码、用户权限、MySQL配置文件及防火墙设置,可以有效解决此问题。希望本文能帮助您成功连接MySQL数据库。
456 4
|
4月前
|
安全 关系型数据库 MySQL
【赵渝强老师】MySQL的连接方式
本文介绍了MySQL数据库服务器启动后的三种连接方式:本地连接、远程连接和安全连接。详细步骤包括使用root用户登录、修改密码、创建新用户、授权及配置SSL等。并附有视频讲解,帮助读者更好地理解和操作。
749 1
|
5月前
|
SQL Java 关系型数据库
java连接mysql查询数据(基础版,无框架)
【10月更文挑战第12天】该示例展示了如何使用Java通过JDBC连接MySQL数据库并查询数据。首先在项目中引入`mysql-connector-java`依赖,然后通过`JdbcUtil`类中的`main`方法实现数据库连接、执行SQL查询及结果处理,最后关闭相关资源。
473 6
|
5月前
|
SQL JavaScript 关系型数据库
node博客小项目:接口开发、连接mysql数据库
【10月更文挑战第14天】node博客小项目:接口开发、连接mysql数据库
|
5月前
|
Java 关系型数据库 MySQL
【编程基础知识】Eclipse连接MySQL 8.0时的JDK版本和驱动问题全解析
本文详细解析了在使用Eclipse连接MySQL 8.0时常见的JDK版本不兼容、驱动类错误和时区设置问题,并提供了清晰的解决方案。通过正确配置JDK版本、选择合适的驱动类和设置时区,确保Java应用能够顺利连接MySQL 8.0。
473 1
|
5月前
|
Java 关系型数据库 MySQL
springboot学习五:springboot整合Mybatis 连接 mysql数据库
这篇文章是关于如何使用Spring Boot整合MyBatis来连接MySQL数据库,并进行基本的增删改查操作的教程。
969 0
springboot学习五:springboot整合Mybatis 连接 mysql数据库