案例:2019年在对数据库中间件ProxySQL滚动升级时,另一台虚机意外重启导致ProxySQL服务不可用,上层服务出现大量异常访问慢的情况的,通过jstack分析线程情况发现业务系统停止在JDBC的调用上,日志中大量的waiting状态的线程;导致服务长时间处于不可以用的状态,后经过重启服务解决问题;

为什么数据库中间件出现问题之后应用服务器需要重启才能解决访问异常和访问慢的问题?为什么数据库恢复之后程序不能自动连接?

分析:

1、程序和mysql数据库之间通信采取的长连接的形式,一般在建立连接的时候会设置一个超时时间(connectTimeout),适用于网络状况正常的情况下,两端连接所用的时间,如果超过这个时间就会主动断开。在应用服务器启动的时候会初始化若干个连接,这些连接和数据库一直保持着长连接,当程序有数据库请求时会从连接池获取一个可用的连接。

2、JDBC与数据库通信是采用Socket方式处理的,数据库当做一个SocketServer的提供方.

3、当SocketServer返回数据的时候(类似于SQL结果集的返回)其流程是:服务端程序数据(数据库) -> 内核Socket Buffer -> 网络 -> 客户端Socket Buffer -> 客户端程序JDBC所在的JVM内存; 在数据库被突然停掉或是发生网络错误时由于TCP/IP的结构原因,socket没有办法探测到网络错误,因此应用也无法主动发现数据库连接断开。如果没有设置socket timeout的话,应用在数据库返回结果前会无期限地等下去,这种连接被称为dead connection。

4、当底层数据库中间库出问题之后JDBC连接数据没有设置超时时间(connectTimeout)就不能及时反馈连接已经中断,会一直等待中;这个时候数据库连接中初始化的连接也处于不知道连接已经中断的情况,当程序需要操作数据库的时候从连接池中获取连接去使用就会报错

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure  
The last packet sent successfully to the server was 0 milliseconds ago. 
The driver has not received any packets from the server

连接池中的连接被数据库关闭了,应用通过连接池getConnection时,都可能获取到这些不可用的连接,且这些连接如果不被其他线程回收的话,它们不会被连接池被废除,也不会重新被创建,占用了连接池的名额。项目本身作为服务端,数据库链接被关闭,客户端调用服务端就会出现 大量的timeout,客户端设置了超时时间,然而主动断开,服务端必然出现close_wait ,由于tomcat 默认最大线程数是200(server.tomcat.max-threads=200),很快就挂掉。

5、服务在启动的时候 jdbc的connectiontimeout、sockettimeout如果没有设置默认为0,是永远不会超时的。数据库或者网络出现异常程序就会一直等待中;

概念:

connectTimeout

指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间。

socketTimeout

指客户端从服务器读取数据的timeout,超出后会抛出SocketTimeOutException

超时时间配置实例:

jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF8&connectTimeout=60000&socketTimeout=60000

JDBC常用配置

  1. user 数据库用户名(用于连接数据库)
  2. password 用户密码(用于连接数据库)
  3. useUnicode是否使用Unicode字符集
  4. characterEncoding 当useUnicode设置为true时,指定字符编码
  5. autoReconnect 当数据库连接异常中断时,是否自动重新连接
  6. autoReconnectForPools 是否使用针对数据库连接池的重连策略
  7. failOverReadOnly 自动重连成功后,连接是否设置为只读
  8. maxReconnects autoReconnect设置为true时,重试连接的次数
  9. initialTimeout autoReconnect设置为true时,两次重连之间的时间间隔,单位:秒
  10. connectTimeout 和数据库服务器建立socket连接时的超时,单位:毫秒, 0表示永不超时
  11. socket操作(读写)超时,单位:毫秒,0表示永不超时

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐