MySQL使用ReplicationConnection导致的连接失效分析与解决
MySQL数据库读写分离 , 是提高服务质量的常用手段之一 , 而对于技术方案 , 有很多成熟开源框架或方案 , 例如:sharding-jdbc、spring中的AbstractRoutingDatasource、MySQL-Router等 , 而mysql-jdbc中的ReplicationConnection亦可支持 。本文暂不对读写分离的技术选型做过多的分析 , 只是探索在使用druid作为数据源、结合ReplicationConnection做读写分离时 , 连接失效的原因 , 并找到一个简单有效的解决方案 。
- 问题背景

文章插图
从日志不难看出 , 这是由于该连接长时间未和MySQL服务端交互 , 服务端已将连接关闭 , 典型的连接失效场景 。
涉及的主要配置如下:jdbc配置
jdbc:mysql:replication://master_host:port,slave_host:port/database_name
druid配置
testWhileIdle=true(即 , 开启了空闲连接检查);
timeBetweenEvictionRunsMillis=6000L(即 , 对于获取连接的场景 , 如果某连接空闲时间超过1分钟 , 将会进行检查 , 如果连接无效 , 将抛弃后重新获取) 。
附:
DruidDataSource.getConnectionDirect中 , 处理逻辑如下:
【MySQL使用ReplicationConnection导致的连接失效分析与解决】
if (testWhileIdle) {final DruidConnectionHolder holder = poolableConnection.holder;long currentTimeMillis= System.currentTimeMillis();long lastActiveTimeMillis= holder.lastActiveTimeMillis;long lastExecTimeMillis= holder.lastExecTimeMillis;long lastKeepTimeMillis= holder.lastKeepTimeMillis;if (checkExecuteTime&& lastExecTimeMillis != lastActiveTimeMillis) {lastActiveTimeMillis = lastExecTimeMillis;}if (lastKeepTimeMillis > lastActiveTimeMillis) {lastActiveTimeMillis = lastKeepTimeMillis;}long idleMillis= currentTimeMillis - lastActiveTimeMillis;long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;if (timeBetweenEvictionRunsMillis <= 0) {timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;}if (idleMillis >= timeBetweenEvictionRunsMillis|| idleMillis < 0 // unexcepted branch) {boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);if (!validate) {if (LOG.isDebugEnabled()) {LOG.debug("skip not validate connection.");}discardConnection(poolableConnection.holder);continue;}}}
mysql超时参数配置wait_timeout=3600(3600秒 , 即:如果某连接超过一个小时和服务端没有交互 , 该连接将会被服务端kill) 。
显而易见 , 基于如上配置 , 按照常规理解 , 不应该出现“The last packet successfully received from server was xxx,xxx,xxx milliseconds ago”的问题 。(当然 , 当时也排除了人工介入kill掉数据库连接的可能) 。
当“理所应当”的经验解释不了问题所在 , 往往需要跳出可能浮于表面经验束缚 , 来一次追根究底 。那么 , 该问题的真正原因是什么呢?
- 本质原因
- 原因分析

文章插图
对于以“jdbc:mysql:replication://”开头配置的jdbc-url , 通过mysql-jdbc获取到的连接 , 其实是一个ReplicationConnection的代理对象 , 默认情况下 , “jdbc:mysql:replication://”后的第一个host和port对应master连接 , 其后的host和port对应slaves连接 , 而对于存在多个slave配置的场景 , 默认使用随机策略进行负载均衡 。
ReplicationConnection代理对象 , 使用JDK动态代理生成的 , 其中InvocationHandler的具体实现 , 是
ReplicationConnectionProxy , 关键代码如下:
public static ReplicationConnection createProxyInstance(List<String> masterHostList, Properties masterProperties, List<String> slaveHostList,Properties slaveProperties) throws SQLException {ReplicationConnectionProxy connProxy = new ReplicationConnectionProxy(masterHostList, masterProperties, slaveHostList, slaveProperties);return (ReplicationConnection) JAVA.lang.reflect.Proxy.newProxyInstance(ReplicationConnection.class.getClassLoader(), INTERFACES_TO_PROXY, connProxy); }
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 架构师必备:MySQL主从同步原理和应用
- 美的回应热水器电晕女生-热水器已过安全使用年限
- 徕卡|王化分享小米12 Ultra使用体验:一个字“稳”
- 全合成机油6个月,5-40全合成机油多久换一次-
- 婴儿尿裤怎么使用
- 手机用户如何使用北斗导航与定位,支持北斗导航的手机怎么使用北斗导航-
- 手机自动答题软件怎么使用,用手机答题用什么软件-
- 脱毛膏越长越多是怎么回事?使用脱毛膏的注意事项
- 手膜护手霜使用方法,护手霜手膜怎么做步骤-
- 新买的铁锅怎么保养不生锈,你知道正确使用和保养铁锅的方法吗-