自动杀死长时间运行的查询(MySql),Apache Tomcat DataSource
Posted
技术标签:
【中文标题】自动杀死长时间运行的查询(MySql),Apache Tomcat DataSource【英文标题】:Automatically kill long running queries (MySql), Apache Tomcat DataSource 【发布时间】:2017-09-11 11:31:53 【问题描述】:我们的 Java Web 应用程序具有搜索功能,允许用户在大型数据库中搜索记录。
如果用户指定了错误的搜索参数,他们最终会得到一个似乎永远不会结束的查询,因为它需要几个小时才能运行。
这是一个 Web 应用程序,因此他们一次又一次地尝试,并且查询占用了所有资源,从而导致严重的性能问题。
如果查询运行时间过长或占用过多 CPU,有什么方法可以自动终止查询?
【问题讨论】:
见***.com/questions/18253934/… 【参考方案1】:对您来说最好的选择是捕获无法执行的搜索条件。
在从 5.7.8 开始的 mysql 中,有一个 max_execution_time setting。
您还可以提出一些 cron 脚本来检查 SHOW PROCESSLIST
并处理处理时间超过您的时间限制的查询。
【讨论】:
【参考方案2】:此答案适用于 Apache tomcat-jdbc 数据源提供程序。
首先你需要了解 PoolProperties
setRemoveAbandonedTimeout
setRemoveAbandoned
当查询花费的时间超过 setRemoveAbandonedTimeout(int) 中指定的时间时,执行该查询的连接被标记为 Abandon 并调用 java.sql.Connection.close() 方法,该方法将一直等待查询完成之前释放连接。
我们可以实现自己的处理程序来处理废弃的连接。以下是改动
首先我们需要添加一个接口
package org.apache.tomcat.jdbc.pool;
public interface AbandonedConnectionHandler
public void handleQuery(Long connectionId);
tomcat-jdbc 文件更改:
PoolConfiguration.java(接口)
添加getter和setter方法。
public void setAbandonedConnectionHandler(AbandonedConnectionHandler abandonedConnectionHandler);
public AbandonedConnectionHandler getAbandonedConnectionHandler();
将这些方法覆盖到所有实现类
DataSourceProxy.java PoolProperties.java org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.java在 org.apache.tomcat.jdbc.pool.PooledConnection.java 中添加方法 getConnectionId()
public Long getConnectionId()
try
//jdbc impl has getId()
Method method = this.connection.getClass().getSuperclass().getMethod("getId");
return (Long)method.invoke(this.connection);
catch (Exception e)
log.warn(" Abandoned QueryHandler failed to initialize connection id ");
return null;
上面的反射代码可能因mysql驱动不同而不同。
现在我们需要在 org.apache.tomcat.jdbc.pool.ConnectionPool.java 中调用 java.sql.Connection.close() 方法之前放置我们的处理程序
将启动废弃连接清理器的 ConnectionPool.java 方法是
protected void abandon(PooledConnection con)
在调用 release(con);
之前在此方法内添加以下代码if(getPoolProperties().getAbandonedConnectionHandler() != null)
con.lock();
getPoolProperties().getAbandonedConnectionHandler().handleQuery(con.getConnectionId());
现在您所要做的就是在创建 tomcat-jdbc 数据源时将您的handerInstance 与 PoolProperties 一起传递。
p.setAbandonedConnectionHandler(new ConnectionHandler(true));
这是我的 AbandonedConnectionHandler 实现。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.AbandonedConnectionHandler;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
public class ConnectionHandler implements AbandonedConnectionHandler
private static final Log log = LogFactory.getLog(ConnectionHandler.class);
private Boolean isAllowedToKill;
private PoolConfiguration poolProperties;
public ConnectionHandler(Boolean isAllowedToKill)
this.isAllowedToKill = isAllowedToKill;
@Override
public void handleQuery(Long connectionId)
Connection conn = null;
Statement stmt = null;
if(this.isAllowedToKill)
try
Class.forName(poolProperties.getDriverClassName());
conn = DriverManager.getConnection(poolProperties.getUrl(),poolProperties.getUsername(),poolProperties.getPassword());
Statement statement = conn.createStatement();
ResultSet result = statement.executeQuery("SELECT ID, INFO, USER, TIME FROM information_schema.PROCESSLIST WHERE ID=" + connectionId);
if(result.next())
if(isFetchQuery(result.getString(2)))
statement.execute("Kill "+connectionId);
statement.close();
conn.close();
catch(Exception e)
e.printStackTrace();
finally
try
if(stmt != null && !stmt.isClosed())
stmt.close();
catch (SQLException e)
log.warn("Exception while closing Statement ");
try
if(conn != null && !conn.isClosed() )
conn.close();
catch (SQLException e)
log.warn("Exception while closing Connection ");
private Boolean isFetchQuery(String query)
if(query == null)
return true;
query = query.trim();
return "SELECT".equalsIgnoreCase(query.substring(0, query.indexOf(' ')));
public PoolConfiguration getPoolProperties()
return poolProperties;
public void setPoolProperties(PoolConfiguration poolProperties)
this.poolProperties = poolProperties;
【讨论】:
以上是关于自动杀死长时间运行的查询(MySql),Apache Tomcat DataSource的主要内容,如果未能解决你的问题,请参考以下文章