我们的java网络应用程序具有搜索功能,允许用户通过大型数据库搜索记录。
如果用户指定了错误的搜索参数,他们最终会得到一个看起来永远不会结束的查询,因为它需要几个小时才能运行。
它是一个Web应用程序,因此他们反复尝试,查询会导致所有资源陷入严重的性能问题。
如果查询运行时间过长或使用太多CPU,有没有办法自动终止查询?
答案 0 :(得分:1)
最适合您的方法是捕捉无法执行的搜索条件。
在MySQL从5.7.8开始,有一个max_execution_time setting。
此外,您可以提供一些cron脚本来检查SHOW PROCESSLIST
并处理超出时间限制的正在处理的查询。
答案 1 :(得分:0)
这个答案适用于Apache tomcat-jdbc DataSource提供程序。
首先,您需要了解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();
将这些方法覆盖到所有实现类
将方法 getConnectionId()添加到 org.apache.tomcat.jdbc.pool.PooledConnection.java
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 DataSource时将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;
}
}