在线上 有些sql执行的比较慢,希望可以指定超时时间, 取消这个sql的执行,以下以druid为例为实现这个功能
jdbc 有2个超时时间, 一个是queryTimeout, 一个是socketTimeout,
queryTimeout的作用是sql执行超时之后,可以取消这次的执行,底层原理是发送kill $id通知mysql来中断这个sql的执行
socketTimeout的作用主要是为了解决tcp连接超时的问题, 超时之后,这个tcp连接会断开
要注意的是,socket超时只是释放tcp链接, 但sql还是在mysql里面执行(可以通过show processlist来看到)
所以为了取消sql的执行,socketTimeout的时间一定要大于queryTimeout才有意义
queryTimeout会抛出异常com.mysql.jdbc.exceptions.MySQLTimeoutException: Statement cancelled due to timeout or client request
socketTimeout会抛出异常com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet successfully received from the server was 2,020 milliseconds ago. The last packet sent successfully to the server was 2,010 milliseconds ago.
可以看具体示例
public class JdbcTimeout {
DruidDataSource dataSource = new DruidDataSource();
@Before
public void setUp() {
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/case?useUnicode=true&characterEncoding=utf-8&socketTimeout=20000000");
dataSource.setQueryTimeout(5);
System.out.println("started");
}
@Test
public void testQuery() throws Exception {
try(Connection conn = dataSource.getConnection() ) {
try(Statement stmt = conn.createStatement()) {
String sql = "SELECT sleep(30*1000), 'name' ";
try(ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
String name = rs.getString("name");
System.out.println(name);
}
}
}
}
}
@Test
public void testUpdate() throws Exception {
String sql = "UPDATE person SET age=3 WHERE id=1 AND SLEEP(30*1000)";
try(Connection conn = dataSource.getConnection() ) {
try(Statement stmt = conn.createStatement()) {
int updated = stmt.executeUpdate(sql);
System.out.println(updated);
}
}
}
}