如何解决JDBC驱动方式连接DDM异常问题
MySQL驱动( JDBC)通过Loadbalance方式连接DDM,在某些场景下连接切换时会陷入死循环,最终导致栈溢出。
问题定位
- 查看APP日志,定位异常原因。
例如,从以下日志中分析出异常最终原因为栈溢出。
Caused by: java.lang.StackOverflowError at java.nio.HeapByteBuffer.<init>(HeapByteBuffer.java:57) at java.nio.ByteBuffer.allocate(ByteBuffer.java:335) at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:795) at java.nio.charset.Charset.encode(Charset.java:843) at com.mysql.jdbc.StringUtils.getBytes(StringUtils.java:2362) at com.mysql.jdbc.StringUtils.getBytes(StringUtils.java:2344) at com.mysql.jdbc.StringUtils.getBytes(StringUtils.java:568) at com.mysql.jdbc.StringUtils.getBytes(StringUtils.java:626) at com.mysql.jdbc.Buffer.writeStringNoNull(Buffer.java:670) at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2636)
- 分析溢出源。
例如,从以下日志可以分析出,溢出原因为驱动内部陷入死循环。
at com.mysql.jdbc.LoadBalancedConnectionProxy.pickNewConnection(LoadBalancedConnectionProxy.java:344) at com.mysql.jdbc.LoadBalancedAutoCommitInterceptor.postProcess(LoadBalancedAutoCommitInterceptor.java:104) at com.mysql.jdbc.MysqlIO.invokeStatementInterceptorsPost(MysqlIO.java:2885) at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2808) at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2483) at com.mysql.jdbc.ConnectionImpl.setReadOnlyInternal(ConnectionImpl.java:4961) at com.mysql.jdbc.ConnectionImpl.setReadOnly(ConnectionImpl.java:4954) at com.mysql.jdbc.MultiHostConnectionProxy.syncSessionState(MultiHostConnectionProxy.java:381) at com.mysql.jdbc.MultiHostConnectionProxy.syncSessionState(MultiHostConnectionProxy.java:366) at com.mysql.jdbc.LoadBalancedConnectionProxy.pickNewConnection(LoadBalancedConnectionProxy.java:344)
- 查看使用的MySQL版本,为5.1.44。
查看该版本源代码,发现获取连接时,LoadBalance会根据负载均衡策略更新连接,并将老连接的配置复制给新连接,在新连接AutoCommit为true,新连接部分参数和老连接不一致,loadBalanceAutoCommitStatementThreshold参数没有配置的场景下,会陷入死循环,更新连接函数调用同步参数函数,同步参数又调用更新连接,最终导致栈溢出。
解决方法
在连接DDM的URL添加loadBalanceAutoCommitStatementThreshold=5&retriesAllDown=10参数。
//使用负载均衡的连接示例 //jdbc:mysql:loadbalance://ip1:port1,ip2:port2..ipN:portN/{db_name} String url = "jdbc:mysql:loadbalance://192.168.0.200:5066,192.168.0.201:5066/db_5133?loadBalanceAutoCommitStatementThreshold=5&retriesAllDown=10";