1 错误原因
1.1 两个参数
MySQL服务端两个参数控制连接超时时间:
- wait_timeout
- interactive_timeout
1.1.1 如何查看
show global variables like 'interactive_timeout'
show global variables like 'wait_timeout'
复制代码
1.1.2 含义与区别
wait_timeout:当一个连接处于空闲状态时,MySQL服务器在关闭连接之前等待的秒数。如果在这段时间内没有任何活动,MySQL将关闭连接。默认值28800秒(8小时)
interactive_timeout:当一个连接处于交互状态时,MySQL服务器在关闭连接之前等待的秒数。如果在这段时间内没有任何活动,MySQL将关闭连接。默认值为28800秒(8小时)
wait_timeout适用于非交互式连接,例如在应用程序中使用连接。interactive_timeout适用于交互式连接,例如在命令行中使用连接
1.2 核心原因
-
程序通过连接池与MySQL建立一个连接A
-
MySQL通过wait_timeout维护连接A超时时间等于8小时
-
假设应用程序连接池维护连接A超时时间等于16小时
-
一段时间后连接A空闲时间已超过8小时但不足16个小时
-
连接池认为连接A可用,继续使用连接A
-
连接A此时已经被MySQL关闭所以报错
2 解决方案
根据上述原因分析我们知道,解决方案核心是设置合理的连接池连接超时时间,本章节通过Druid连接池进行说明。
2.1 配置官方文档
https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8
复制代码
2.2 关注参数
- testOnBorrow
- testOnReturn
- testWhileIdle
- timeBetweenEvictionRunsMillis
- minEvictableIdleTimeMillis
2.3 参数配置
解决方案其实不复杂:不用去设置超时时间而是开启testWhileIdle
,当申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis
,执行validationQuery
检测连接是否有效:
<property name="validationQuery" value="SELECT 1" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="testWhileIdle" value="true" />
复制代码
3 延伸知识
3.1 错误识别
- 常见场景
- 新应用访问量不高
- 测试环境(第二天刚上班时访问)
- 错误特征
- 上线一段时间才报错
- 这类错误时隐时现
- 报错不是业务错误
- 错误与数据库底层相关
3.2 池化访问常见错误
这种问题不仅出现在访问MySQL时,在访问其它一些数据源时也可能出现这个问题,例如ES与MongoDB等等。这是因为访问数据源一般都会使用连接池。
这些客户端的连接池可能没有类似testWhileIdle
参数,这种情况下需要设置合理超时时间或者keep-alive时间解决,这需要结合数据源配置文档具体分析。