适用版本
OceanBase 数据库所有版本。
问题描述
OceanBase 数据库在进行业务 commit 时遇到如下异常报错。
Communications link failure during commit(). Transaction resolution unknown
问题原因
调用 commit 函数时异常导致,commit 函数主要向数据库发送 commit 请求,执行异常报错。
Communications link failure during commit(). Transaction resolution unknown
由此可知,在这个时间点,任何导致 execSQL 异常的情况,都会触发该抛出。常见的异常的情况如下。
- commit 执行时间过长,请求被终止(kill 操作)或连接被断开(各类超时参数设置)。
- 使用的连接已经异常,但是 jdbc 未能判断,依然使用异常连接执行 commit 时,commit 执行失败。
- 其他未知原因导致,如客户端、网络等导致,此类不常见,但是存在可能性。
排查过程
通过排查 ODP 的日志,可以确认 ODP 的 digest 日志中,没有执行时间超过 2 秒的 SQL,而程序的 sockettimeout 为 10 秒,可以排除是第一点导致。
下面主要测试和验证第二点。
编写 java 测试代码,通过 interactive_timeout 模拟远端断链,观察 commit 的执行情况。
-- 数据库 interactive_timeout:10s public class TimeOutTest { public static void main(String[] args) { Connection con; String driver="com.mysql.jdbc.Driver"; String url="jdbc:mysql://rm-xxxx.mysql.rds.aliyuncs.com:3306/alitest?&useSSL=false&serverTimezone=UTC&socketTimeout=50000&interactiveClient=true"; String user="xxxx"; String password="xxxx"; try { Class.forName(driver); con = DriverManager.getConnection(url, user, password); if (!con.isClosed()) { System.out.println("数据库连接成功"); Statement statement = con.createStatement(); String sql = "select * from teacher"; //Thread.sleep(30000); //String sql = "select sleep(10000)"; con.setAutoCommit(false); ResultSet resultSet = statement.executeQuery(sql); while (resultSet.next()){ System.out.print(resultSet.getInt("a")); System.out.print(resultSet.getInt("b")); System.out.println(resultSet.getInt("c")); } Thread.sleep(20000); con.commit(); resultSet.close(); statement.close(); con.close(); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }OBServer 参数 interactive_timeout 设置为 20s,会在应用线程执行 Thread.sleep(20000) 时主动断链。而断链后,应用继续执行 commit 语句,则报如下异常。
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Communications link failure during commit(). Transaction resolution unknown. at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480) at com.mysql.jdbc.Util.handleNewInstance(Util.java:403) at com.mysql.jdbc.Util.getInstance(Util.java:386) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:919) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:898) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:887) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:861) at com.mysql.jdbc.ConnectionImpl.commit(ConnectionImpl.java:1549) at TimeOutTest.main(TimeOutTest.java:34)通过抓包也可以判断这点。

由此可见,此问题在于代码逻辑导致,非 ODP 执行耗时长。
解决方法
修改代码逻辑。