Mysql JDBC-mysql-Driver queryTimeout分析
阿新 • • 發佈:2018-10-22
erro ecan nag get ava timer oca element mysql
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中延遲執行
locallyScopedConn.getCancelTimer().schedule(timeoutTask, (long)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,說明確實超時了。
Mysql JDBC-mysql-Driver queryTimeout分析