`
iwinit
  • 浏览: 455047 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

[mysql driver]query timeout实现

阅读更多

之前一直对jdbc的query timeout有疑惑,看了下mysql的实现,大概了解了。

关于jdbc的各种超时时间关系见这个文章http://www.importnew.com/2466.html,这里简单来看一下mysql的query timeout实现,也是比较简单。query timeout就是在发起请求前,启动一个定时任务,触发之后抛出异常。

核心代码再执行sql过程中

protected synchronized ResultSetInternalMethods executeInternal(int maxRowsToRetrieve,
			Buffer sendPacket, boolean createStreamingResultSet,
			boolean queryIsSelectOnly, Field[] metadataFromCache,
			boolean isBatch)
			throws SQLException {
		try {
			//恢复超时状态
			resetCancelledState();
			//物理连接
			MySQLConnection locallyScopedConnection = this.connection;
			
			this.numberOfExecutions++;
	
			if (this.doPingInstead) {
				doPingInstead();
				
				return this.results;
			}
			
			ResultSetInternalMethods rs;
			
			CancelTask timeoutTask = null;
	
			try {
				//如果设置了query timeout,则启动一个timer任务
				if (locallyScopedConnection.getEnableQueryTimeouts() &&
						this.timeoutInMillis != 0
						&& locallyScopedConnection.versionMeetsMinimum(5, 0, 0)) {
					timeoutTask = new CancelTask(this);
					locallyScopedConnection.getCancelTimer().schedule(timeoutTask, 
							this.timeoutInMillis);
				}
				//这里执行sql,因为mysql是bio的,所以正常的话线程会等待server端返回,要么就socket read timeout抛出异常
				rs = locallyScopedConnection.execSQL(this, null, maxRowsToRetrieve, sendPacket,
					this.resultSetType, this.resultSetConcurrency,
					createStreamingResultSet, this.currentCatalog,
					metadataFromCache, isBatch);
				//如果需要超时,即使服务端正常返回了,还是会抛出超时异常
				if (timeoutTask != null) {
					//先把timer任务停了,大部分情况下超时任务还没到点触发
					timeoutTask.cancel();
					
					locallyScopedConnection.getCancelTimer().purge();
					//如果在进行超时操作的时候抛出了异常,则直接抛出这个异常
					if (timeoutTask.caughtWhileCancelling != null) {
						throw timeoutTask.caughtWhileCancelling;
					}
					
					timeoutTask = null;
				}
				synchronized (this.cancelTimeoutMutex) {
					if (this.wasCancelled) {
						SQLException cause = null;
						
						//如果被超时处理了,则抛出MySQLTimeoutException
						if (this.wasCancelledByTimeout) {
							cause = new MySQLTimeoutException();
						} 
						//如果是被cancel了,就抛出cancel异常						
						else {
							cause = new MySQLStatementCancelledException();
						}
						//完了之后,重新恢复状态,等待下一次sql请求
						resetCancelledState();
						//抛出异常
						throw cause;
					}
				}
			} finally {
				//最后的时候,取消超时任务,大部分场景都是正常访问,timer没到点就返回了
				if (timeoutTask != null) {
					timeoutTask.cancel();
					locallyScopedConnection.getCancelTimer().purge();
				}
			}
			
			return rs;
		} catch (NullPointerException npe) {
			checkClosed(); // we can't synchronize ourselves against async connection-close
			               // due to deadlock issues, so this is the next best thing for
			 			   // this particular corner case.
			
			throw npe;
		}
	}

 具体的超时实现CancelTask

public void run() {
					//超时的时候,杀连接
					if (connection.getQueryTimeoutKillsConnection()) {
						try {
							toCancel.wasCancelled = true;
							toCancel.wasCancelledByTimeout = true;
							connection.realClose(false, false, true, 
									new MySQLStatementCancelledException(Messages.getString("Statement.ConnectionKilledDueToTimeout")));
						} catch (NullPointerException npe) {
							// not worth guarding against
						} catch (SQLException sqlEx) {
							caughtWhileCancelling = sqlEx;
						}
					} else {
						//正常的关闭当前sql请求
						Connection cancelConn = null;
						java.sql.Statement cancelStmt = null;
	
						try {
							synchronized (cancelTimeoutMutex) {
								//复制一个连接,这里会重新创建新的连接,如果此时db down了,则会connect timeout。然后发送kill指令,如果db down了,发送不成功,抛出异常,此时cancel失败
								if (origConnURL == connection.getURL()) {
									//All's fine
									cancelConn = connection.duplicate();
									cancelStmt = cancelConn.createStatement();
									cancelStmt.execute("KILL QUERY " + connectionId);
								} else {
									try {
										cancelConn = (Connection) DriverManager.getConnection(origConnURL, origConnProps);
										cancelStmt = cancelConn.createStatement();
										cancelStmt.execute("KILL QUERY " + connectionId);
									} catch (NullPointerException npe){
										//Log this? "Failed to connect to " + origConnURL + " and KILL query"
									}
								}
								//设置状态,方便主线程进行超时判断
								toCancel.wasCancelled = true;
								toCancel.wasCancelledByTimeout = true;
							}
						}
						//如果超时任务抛出异常,比如db挂了,则给主线程一个异常						
						 catch (SQLException sqlEx) {
							caughtWhileCancelling = sqlEx;
						} catch (NullPointerException npe) {
							// Case when connection closed while starting to cancel
							// We can't easily synchronize this, because then one thread
							// can't cancel() a running query
	
							// ignore, we shouldn't re-throw this, because the connection's
							// already closed, so the statement has been timed out.
						} finally {
							if (cancelStmt != null) {
								try {
									cancelStmt.close();
								} catch (SQLException sqlEx) {
									throw new RuntimeException(sqlEx.toString());
								}
							}
	
							if (cancelConn != null) {
								try {
									cancelConn.close();
								} catch (SQLException sqlEx) {
									throw new RuntimeException(sqlEx.toString());
								}
							}
							
							toCancel = null;
							origConnProps = null;
							origConnURL = null;
						}
					}
				}

从上可知,如果发送sql请求之后db挂了,query timeout触发时还没恢复,则query timeout不成功,此时主线程还会等待server端返回直到socket read timeout抛出异常,而如果socket read timeout设置比query timeout小,则query timeout始终无效,因为超时任务还没启动,主线程就抛出socket timeout异常了。

分享到:
评论

相关推荐

    JBoss7.1.1配置MySql数据源

    <set-tx-query-timeout>false</set-tx-query-timeout> <blocking-timeout-millis>30000</blocking-timeout-millis> <idle-timeout-minutes>30</idle-timeout-minutes> </timeout> ...

    Jmeter连接mysql数据库jar包mysql-connector-java-5.1.48-bin.zip

    - **Connection Timeout** 和 **Read Timeout**:设置连接和读取超时时间。 2. **mysql-connector-java**:这是MySQL的Java驱动程序,用于在Java应用(比如JMeter)中与MySQL通信。在本例中,提供的压缩包文件`...

    embulk之sqlserver to mysql

    ### 使用Embulk实现SQL Server至MySQL的数据迁移 #### 一、背景介绍 随着业务发展和技术迭代,企业常常需要在不同的数据库之间进行数据迁移。本文主要介绍如何使用Embulk这一工具来实现从SQL Server到MySQL的数据...

    Java使用C3P0连接MySQL数据库查询

    c3p0.checkout_timeout=5000 ``` 这些参数定义了连接池的大小、获取连接的增量、空闲连接测试间隔以及超时设置等。 然后,在Java代码中,我们可以创建C3P0Util工具类,初始化数据源并提供数据库操作的方法: ```...

    jdbc操作Mysql数据库demo

    它允许Java应用程序连接并执行SQL语句,实现数据的CRUD(Create、Read、Update、Delete)操作。本教程将以MySQL数据库为例,介绍如何使用JDBC进行数据库操作。 首先,我们需要理解JDBC的基本步骤: 1. **加载驱动*...

    php操纵mysqli数据库的实现方法

    $mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 2); // 设置超时时间为2秒 $mysqli->real_connect('127.0.0.1', 'root', '', 'test'); ``` 执行SQL语句可以通过`$mysqli->query()`方法完成。对于无结果集的SQL(如...

    properies文件jdbc驱动

    query.timeout=10000 ``` 7. **其他高级配置**:根据具体数据库驱动,可能还有其他特定参数,如自动提交、字符编码等: ``` autoCommit=true characterEncoding=utf-8 ``` 在Java程序中,我们通常会使用`...

    java通用jdbc配置文件

    6. **其他配置**:还可以包含其他特定的属性,比如自动提交的设置(autocommit)、事务隔离级别(transaction isolation level)、查询超时时间(query timeout)等。 在Java项目中,我们通常会使用`Properties`类...

    java高并发秒杀系统.rar

    idle-timeout: 18000 # 最大连接数,默认10 maximum-pool-size: 10 # 从连接池返回的连接自动提交 auto-commit: true # 连接最大存活时间,0表示永久存活,默认1800000(30分钟) max-lifetime: 1800000 # ...

    jmeter JDBC Request

    - **Query timeout**:设置查询超时时间。 - **Handle result set**:定义如何处理Callable Statements返回的结果。 ### 示例:SELECT请求 对于一个简单的SELECT查询,如`SELECT * FROM plf_users`,在设置好JDBC ...

    Spring Boot与HikariCP:性能卓越的数据库连接池

    - `connection-timeout`: 获取连接的最大等待时间(毫秒)。 - `connection-test-query`: 用于测试连接的SQL语句。 **3.3 创建实体和仓库** 如果使用Spring Data JPA,可以创建一个实体类和对应的仓库接口: ```...

    jmeter性能测试白皮书

    - **Pool Timeout**:连接池中的连接等待时间。 - **Idle Cleanup Interval**:定义了连接池清理空闲连接的时间间隔,即如果一个连接超过这个时间没有被使用,则会被自动关闭。 - **AutoCommit**:决定数据库操作...

    hibernate.properties

    #hibernate.c3p0.timeout 5000 #hibernate.c3p0.max_statements 100 #hibernate.c3p0.idle_test_period 3000 #hibernate.c3p0.acquire_increment 2 #hibernate.c3p0.validate false ############################...

    SpringBoot整合JDBC&Druid;数据源示例

    @Value("${spring.datasource.druid.remove-abandoned-timeout}") private int removeAbandonedTimeout; @Bean public DruidDataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); ...

    proxool实例

    proxool.mytestdriver.create-timeout=30000 proxool.mytestdriver.test-query=SELECT 1 ``` 然后在代码中加载这个配置并初始化Proxool,如下所示: ```java Properties props = new Properties(); props.load(new...

    学JDBC,这一篇就够了.pdf

    System.out.println("ID: " + id + ", Name: " + name); } ``` #### 第五章:数据库工具类JdbcUtils ##### 5.1 需求: 为了简化数据库操作,可以创建一个工具类`JdbcUtils`,用来封装数据库连接的创建和关闭逻辑...

    springboot2.0使用Hikari连接池的方法(替换druid)

    spring.datasource.hikari.connection-timeout=30000 # 获取连接的超时时间(毫秒) spring.datasource.hikari.connection-test-query=SELECT 1 # 用于测试连接是否有效的SQL查询 ``` 这些配置项定义了数据库连接池...

    SpringBoot .yml

    - **`timeout`**:连接超时时间(单位毫秒)。 ##### 5. 数据源配置 ```yaml spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql:...

    jpivot学习总结.doc

    <jp:mondrianQuery dataSource="" id="query01" jdbcDriver="oracle.jdbc.driver.OracleDriver" jdbcUrl="jdbc:oracle:thin:ngykt/ngyktadmin@172.16.46.241:1521:orcl10" catalogUri="/WEB-INF/queries/feeSchema....

Global site tag (gtag.js) - Google Analytics