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分析的更多相关文章

  1. 数据库 MySQL Jdbc JDBC的六个固定步骤

    *0 案例:    a)在JavaScript中使用正则表达式,在JS中正则表达式的定界符是://     var regexp = /^[0-9]+$/;     if(regexp.test(nu ...

  2. JDBC MYSQL 学习笔记(一) JDBC 基本使用

    1.JDBC简单介绍 SUN公司为了简化.统一对数据库的操作,定义了一套Java操作数据库的规范.称之为JDBC. JDBC全称为:Java Data Base Connectivity(java数据 ...

  3. 无法找到类:java.lang.ClassNotFoundException: com.mysql.jdbc.driver

    转载自:http://blog.csdn.net/huangbiao86/article/details/6428608 问题描述:连接数据库,而明明已经将mysql-connector-java-5 ...

  4. MySQL JDBC的queryTimeout坑

    遇到一个MySQL JDBC跑execute规定的方法queryTimeout坑,更恶心,无论是BUG,不能,^_^,为什么要说?请看下面的说明: 现象: 用同一个Connection运行大批量SQL ...

  5. Spring警告: Could not load driverClass com.mysql.jdbc.Driver(待解决)

    在一个Spring项目中,新建了一个外部属性文件db.properties,在xml文件中利用${}来引用db.properties文件里面的属性. beans-properties.xml: < ...

  6. 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- ...

  7. Cannot load JDBC driver class 'com.mysql.jdbc.Driver '

    最近在学JAVA, SSM, 照着网上的例子系统启动后总是报这个错(IDE :IEDA): HTTP Status 500 - Request processing failed; nested ex ...

  8. 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 ...

  9. Class.forName("com.mysql.jdbc.Driver") ;

    try { Class.forName("com.mysql.jdbc.Driver") ; } catch(ClassNotFoundException e) { System. ...

随机推荐

  1. 关于微信小程序官网的使用

    我们在看微信支付相关的东西的时候,会发现有些想找的地址不好找,,没看到入口,接下来我就是整理了一下 链接: https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa ...

  2. Prometheus-Alertmanager告警对接到企业微信

    之前写过将Prometheus的监控告警信息通过Alertmanager推送到钉钉群. 最近转移了阵地,需要将Prometheus监控告警信息推送到企业微信群,经过两天的摸索,以及查了网上的一些资料, ...

  3. 栈帧的内部结构--动态返回地址(Return Address)

    每个栈帧中包含: 局部变量表(Local Variables) 操作数栈(Opreand Stack) 或表达式栈 动态链接 (Dynamic Linking) (或指向运行时常量的方法引用) 动态返 ...

  4. cas机制的原理和使用

    一.什么是cas CAS,compare and swap的缩写,中文翻译成比较并交换. CAS 操作包含三个操作数 -- 内存位置(V).预期原值(A)和新值(B). 如果内存位置的值与预期原值相匹 ...

  5. [GXYCTF2019]禁止套娃 1 &无参数RCE

    [GXYCTF2019]禁止套娃 1 啥都没有那只能上扫描器来一探究竟了. 扫完没有啥结果,但网上找了一下说是git泄露,但是我这里显示了403. <?php include "fla ...

  6. django中外键的related_name属性

    我先定义两个模型,一个是作者,一个是作者出版的书籍,算是一对多的类型. class Person(models.Model); name = models.CharField(verbose_name ...

  7. python协程(yield、asyncio标准库、gevent第三方)、异步的实现

    引言 同步:不同程序单元为了完成某个任务,在执行过程中需靠某种通信方式以协调一致,称这些程序单元是同步执行的. 例如购物系统中更新商品库存,需要用"行锁"作为通信信号,让不同的更新 ...

  8. WEB通信及前后端组成

    NDS(域名系统) 按个人的理解,就是将域名和IP映射,通过输入域名,DNS 找到对应的域名的IP地址,即可访问. NDS请求步骤: 当用户输入域名(例:www.baidu.com),回车那一刻: 1 ...

  9. Java基础——锁

    1.锁 当一个共享资源被多方访问时为了避免发生冲突而施加的一种机制 2.乐观锁和悲观锁 Java中锁在宏观分为乐观锁和悲观锁 乐观锁:是一种乐观思想,认为多读少写,一般情况下数据在修改时不会出现冲突, ...

  10. Salesforce LWC学习(二十七) File Upload

    本篇参考: https://developer.salesforce.com/docs/component-library/bundle/lightning-file-upload/documenta ...