Mysql JDBC-mysql-Driver queryTimeout分析
Mysql jdbc的queryTimeout分析
Mysql的jdbc-driver
com.mysql.jdbc.Driver
设置queryTimeout方法
com.mysql.jdbc.StatementImpl.setQueryTimeout
StatementImpl实例有一个field:timeoutInMillis
public void setQueryTimeout(int seconds) throws SQLException {
    synchronized(this.checkClosed().getConnectionMutex()) {
        if(seconds < 0) {
            throw SQLError.createSQLException(Messages.getString("Statement.21"), "S1009", this.getExceptionInterceptor());
        } else {
            this.timeoutInMillis = seconds * 1000;
        }
    }
}
queryTimeout使用场景示例:
com.mysql.jdbc.StatementImpl.executeQuery
ResultSet executeQuery(String sql) throws SQLException;
executeQuery有一个较复杂的逻辑:
- 获取connection的互斥锁 
- 校验、初始化一些配置,是否为ping请求 
- sql转义,防sql注入 
- 判断timeout是否有效,有效时创建一个CancelTask 
- 将cancelTask放入Timer中延迟执行 - if (locallyScopedConn.getEnableQueryTimeouts() && this.timeoutInMillis != 0 && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
 timeoutTask = new CancelTask(this);
 //每个连接会有一个CancelTimer,一个deamon线程
 locallyScopedConn.getCancelTimer().schedule(timeoutTask, this.timeoutInMillis);
 }
 - 也就是在当前时间的timeoutInMillis后会执行这个Task 
- 执行sql语句,获取结果 
- 超时任务判断,如果有超时任务,分为两种情况:1 超时异常已经抛出,直接返回异常;1 超时任务未执行,cancel超时任务 - this.results = locallyScopedConn.execSQL(this, sql, this.maxRows, (Buffer)null, this.resultSetType, this.resultSetConcurrency, this.createStreamingResultSet(), this.currentCatalog, cachedFields); if(timeoutTask != null) {
 if(timeoutTask.caughtWhileCancelling != null) {
 throw timeoutTask.caughtWhileCancelling;
 } timeoutTask.cancel();
 locallyScopedConn.getCancelTimer().purge();
 timeoutTask = null;
 }
 
- 获取lastInsertId 
- 返回results 
StatementImpl.CancelTask
class CancelTask extends TimerTask {
    SQLException caughtWhileCancelling = null;
    StatementImpl toCancel;
    Properties origConnProps = null;
    String origConnURL = "";
    long origConnId = 0L;
    CancelTask(StatementImpl cancellee) throws SQLException {
        this.toCancel = cancellee;
        this.origConnProps = new Properties();
        Properties props = StatementImpl.this.connection.getProperties();
        Enumeration keys = props.propertyNames();
        while(keys.hasMoreElements()) {
            String key = keys.nextElement().toString();
            this.origConnProps.setProperty(key, props.getProperty(key));
        }
        this.origConnURL = StatementImpl.this.connection.getURL();
        this.origConnId = StatementImpl.this.connection.getId();
    }
    public void run() {
        Thread cancelThread = new Thread() {
            public void run() {
                Connection cancelConn = null;
                java.sql.Statement cancelStmt = null;
                try {
                    MySQLConnection npe = (MySQLConnection)StatementImpl.this.physicalConnection.get();
                    if(npe != null) {
                        if(npe.getQueryTimeoutKillsConnection()) {
                            CancelTask.this.toCancel.wasCancelled = true;
                            CancelTask.this.toCancel.wasCancelledByTimeout = true;
                            npe.realClose(false, false, true, new MySQLStatementCancelledException(Messages.getString("Statement.ConnectionKilledDueToTimeout")));
                        } else {
                            Object var4 = StatementImpl.this.cancelTimeoutMutex;
                            synchronized(StatementImpl.this.cancelTimeoutMutex) {
                                if(CancelTask.this.origConnURL.equals(npe.getURL())) {
                                    cancelConn = npe.duplicate();
                                    cancelStmt = cancelConn.createStatement();
                                    cancelStmt.execute("KILL QUERY " + npe.getId());
                                } else {
                                    try {
                                        cancelConn = (Connection)DriverManager.getConnection(CancelTask.this.origConnURL, CancelTask.this.origConnProps);
                                        cancelStmt = cancelConn.createStatement();
                                        cancelStmt.execute("KILL QUERY " + CancelTask.this.origConnId);
                                    } catch (NullPointerException var25) {
                                        ;
                                    }
                                }
                                CancelTask.this.toCancel.wasCancelled = true;
                                CancelTask.this.toCancel.wasCancelledByTimeout = true;
                            }
                        }
                    }
                } catch (SQLException var27) {
                    CancelTask.this.caughtWhileCancelling = var27;
                } catch (NullPointerException var28) {
                    ;
                } finally {
                    if(cancelStmt != null) {
                        try {
                            cancelStmt.close();
                        } catch (SQLException var24) {
                            throw new RuntimeException(var24.toString());
                        }
                    }
                    if(cancelConn != null) {
                        try {
                            cancelConn.close();
                        } catch (SQLException var23) {
                            throw new RuntimeException(var23.toString());
                        }
                    }
                    CancelTask.this.toCancel = null;
                    CancelTask.this.origConnProps = null;
                    CancelTask.this.origConnURL = null;
                }
            }
        };
        cancelThread.start();
    }
}
timeout后执行的操作主要为:
- cancelConn = npe.duplicate(); //复制一个当前连接配置相同的连接
- cancelStmt = cancelConn.createStatement(); //创建一个Statement对象,用来发送sql语句到数据库
- cancelStmt.execute("KILL QUERY " + npe.getId()); //杀掉已经timeout的语句
可以看到,只要CancelTask执行,除了执行sql的连接压根没有成功生成外,都会执行KILL QUERY操作,里面不做任何请求是否已成功的判断。
原因也比较明显,凡是执行到CancelTask,说明确实超时了。
connectTimeout=5000&socketTimeout=10000
其实,设置了queryTimeout也不一定生效,上述代码中无论是成功执行,还是CancelTask,都会涉及到对socket的操作,socket操作是底层的,它也有timeout选项,错误的配置或不配置,会采用操作系统的默认配置,这个时间可能是长达30分钟的。一旦网络出现问题,调用socket.read()时阻塞了,都到导致应用程序假死。
解决办法
在jdbc.url中配置参数connectTimeout和socketTimeout参数,当然他们的值应该大于计划内的程序执行sql的最长耗时时间,否则可能中断正常的sql执行。
Mysql JDBC-mysql-Driver queryTimeout分析的更多相关文章
- 数据库  MySQL Jdbc  JDBC的六个固定步骤
		*0 案例: a)在JavaScript中使用正则表达式,在JS中正则表达式的定界符是:// var regexp = /^[0-9]+$/; if(regexp.test(nu ... 
- JDBC MYSQL 学习笔记(一) JDBC 基本使用
		1.JDBC简单介绍 SUN公司为了简化.统一对数据库的操作,定义了一套Java操作数据库的规范.称之为JDBC. JDBC全称为:Java Data Base Connectivity(java数据 ... 
- 无法找到类:java.lang.ClassNotFoundException: com.mysql.jdbc.driver
		转载自:http://blog.csdn.net/huangbiao86/article/details/6428608 问题描述:连接数据库,而明明已经将mysql-connector-java-5 ... 
- MySQL JDBC的queryTimeout坑
		遇到一个MySQL JDBC跑execute规定的方法queryTimeout坑,更恶心,无论是BUG,不能,^_^,为什么要说?请看下面的说明: 现象: 用同一个Connection运行大批量SQL ... 
- Spring警告: Could not load driverClass com.mysql.jdbc.Driver(待解决)
		在一个Spring项目中,新建了一个外部属性文件db.properties,在xml文件中利用${}来引用db.properties文件里面的属性. beans-properties.xml: < ... 
- ambari-server启动出现ERROR main] DBAccessorImpl:106  - Error while  creating database accessor  java.lang.ClassNotFoundException:com.mysql.jdbc.Driver问题解决办法(图文详解)
		不多说,直接上干货! 问题详情 ambari-server启动时,报如下的错误 问题分析 注:启动ambari访问前,请确保mysql驱动已经放置在/usr/share/Java内且名字是mysql- ... 
- Cannot load JDBC driver class 'com.mysql.jdbc.Driver '
		最近在学JAVA, SSM, 照着网上的例子系统启动后总是报这个错(IDE :IEDA): HTTP Status 500 - Request processing failed; nested ex ... 
- cloudera-scm-server启动出现Error creating bean with name 'entityManagerFactoryBean'与HHH010003: JDBC Driver class not found: com.mysql.jdbc.Driver错误解决办法(图文详解)
		不多说,直接上干货! 问题详情 -- ::, INFO main:com.cloudera.server.cmf.Main: Starting SCM Server. JVM Args: [-Dlog ... 
- Class.forName("com.mysql.jdbc.Driver") ;
		try { Class.forName("com.mysql.jdbc.Driver") ; } catch(ClassNotFoundException e) { System. ... 
随机推荐
- 2020华为杯数学建模B题-RON建模 赛后总结与分析
			好久好久没有写博客了...挺累的,从二月份开始找暑期实习,接着在进行暑期实习,然后马不停蹄地进行秋招,现在总算结束实习,前两天又参加了华为杯数学建模竞赛,感觉接下来就会很轻松了,希望能好好休息休息.这 ... 
- HotSpot VM执行引擎的实现
			Java代码的执行分类: 第一种是将源代码编译成字节码文件,然后再运行时通过解释器将字节码文件转为机器码执行 第二种是编译执行(直接编译成机器码).现代虚拟机为了提高执行效率,会使用即时编译技术(JI ... 
- Spring Boot 自动配置的原理、核心注解以及利用自动配置实现了自定义 Starter 组件
			本章内容 自定义属性快速入门 外化配置 自动配置 自定义创建 Starter 组件 摘录:读书是读完这些文字还要好好用心去想想,写书也一样,做任何事也一样 图 2 第二章目录结构图 第 2 章 Spr ... 
- Java 常用类-程序员头大的日期时间API
			第二节.日期时间API 一.JDK8之前日期时间API 1.1 java.lang.System类 System类提供的public static long currentTimeMillis()用来 ... 
- Mysql中 int(3) 类型的含义
			注意:这里的(3)代表的并不是存储在数据库中的具体的长度,以前总是会误以为int(3)只能存储3个长度的数字,int(11)就会存储11个长度的数字,这是大错特错的. 其实当我们在选择使用int的类型 ... 
- 090 01 Android 零基础入门  02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门  02 Java面向对象 02 Java封装 01 封装的实现 04 使用包进行类管理(2)——导入包
			090 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ... 
- Arduino Mega 2560
			Arduino Mega 2560 www.theengineeringprojects.com/ 此板子有54个引脚,16个模拟量输入引脚,12个PWM输出引脚,4个串口,带I2C,SPI通讯口,更 ... 
- 电机AB相编码器测速
			控制任务 检测编码器的脉冲并测速 电路设计 图1 直流电机带减速器和编码器 图2 编码器接线定义 编码器接线定义如下 M1:电机电源接口,绿色的 GND:编码器电源负极输入口,橙色的 C1:编码器A ... 
- 一个Java对象的内存布局
			1.对象的创建过程 class loading class linking(verification,preparation,resolution) class initializing 申请对象内存 ... 
- 2018年10月份编程语言排行榜(来自TIOBE Index for October 2018)
			TIOBE Index for October 2018 from:https://www.tiobe.com/tiobe-index// October Headline: Swift is kno ... 
