杀死用户中止的MySQL查询

时间:2011-09-28 11:25:46

标签: php mysql

当从PHP运行长查询时,如果用户在浏览器中按停止, [如何]可以终止查询?

考虑到我不能调用任何其他PHP函数,因为PHP在等待MySQL时被阻止。

此外,由于会话锁定,我无法再向服务器发出请求(通过Ajax)。

所以一个解决方案可能是:

  • 忽略用户中止
  • 在后台运行长查询,如果已完成,则每100毫秒进行一次PHP检查
  • 从查询中获取pid
  • 如果用户中止,请杀死pid
  • 完成后返回结果

我不知道如何做的两件事是:

  • 运行非阻塞(后台)查询
  • 获取查询的pid

6 个答案:

答案 0 :(得分:18)

对于那些感兴趣的人,这是我使用的:

<?php
// Connection to query on
$query_con = mysqli_connect($host, $user, $password, $name, $port);

// Connection to kill on
$kill_con = mysqli_connect($host, $user, $password, $name, $port);

// Start the query
$query_con->query($slow_query, MYSQLI_ASYNC);

// Get the PID
$thread_id = $query_con->thread_id;

// Ignore user abort so we can kill the query
ignore_user_abort(true);

do  {
    // Poll MySQL
    $links = $errors = $reject = array($mysqli->mysqli);
    $poll = mysqli_poll($links, $errors, $reject, 0, 500000);

    // Check if the connection is aborted and the query was killed
    if (connection_aborted() && mysqli_kill($kill_con, $thread_id)) {
        die();
    }
} while (!$poll);

// Not aborted, so do stuff with the result
$result = $link->reap_async_query();
if (is_object($result)) {
    // Select
    while ($row = $result->fetch_object()) {
        var_dump($row);
    }
} else {
    // Insert/update/delete
    var_dump($result);
}

答案 1 :(得分:5)

一旦PHP注意到用户已停止请求(这通常不会发生,直到脚本尝试向用户输出内容),您的脚本将终止。在关闭之前,PHP会调用您激活的所有关闭功能。您可以实现一个可以终止查询的关闭函数,并将其注册到register_shutdown_function()

您可以执行此操作的另一种方法是,在启用ignore_user_abort()的情况下运行脚本,并通过定期调用connection_aborted()来检查用户是否已中止。如果用户已中止,请终止查询并正常退出脚本。

有关连接处理的更多信息here

答案 2 :(得分:3)

如果查询所花费的时间是由于返回的大型数据集,那么您可以使用mysql_unbuffered_query。然后在shutdown_function中,您可以释放结果并断开形式mysql。

如果您使用的是PHP 5.3.x且拥有mysqlnd,则可以在mysqli_query中使用MYSQLI_ASYNC,然后使用mysqli_poll来获取结果。

第一个选项仅在检索数据集时需要帮助。但是如果mysql实际上花了很长时间来解析和创建执行计划并将表加载到内存中它将无济于事。在这种情况下,您需要MYSQLI_ASYNC,该PHP 5.3.x仅适用于mysqlnd WITH session locking

此外,如果您的主要问题是{{1}},请查看有关PHP文档的此帖子http://php.net/manual/en/ref.session.php#64525
你可能会在那里找到一些有用的建议。

答案 3 :(得分:0)

我认为你可以试试mysql KILL

http://dev.mysql.com/doc/refman/5.0/en/kill.html

如果你能够通过用户中止调用某些内容,那么你可以使用KILL通过获取它的进程ID来终止你的mysql连接或查询。

正如你在评论中所说,PHP被阻止,直到mysql返回

所以你可以通过这种方式从javascript获得帮助:

1.使用JS检测用户中止 2.call一个用户中止功能 3.在那个函数中你可以杀死你以前的查询但是在这里你需要上一个查询的进程id所以你可以通过一个技巧来获得它,你可以在运行第一个长查询并将其传递给JS函数时选择进程id并用它来杀死你的查询。

希望它会对你有所帮助。

答案 4 :(得分:0)

在大多数情况下,PHP不会知道连接已关闭(因为用户已中止),直到它尝试通过网络发送内容然后收到SIGPIPE。您唯一能做的就是尝试使用超时中止长查询或从其他进程中删除它们。

答案 5 :(得分:0)

这是使用PDO的示例。

$id = $connection->executeQuery("SELECT CONNECTION_ID()")->fetchColumn();
ignore_user_abort(true);
while($row = $result->fetch()) {
  /* Processing ... */
  if (connection_aborted()) {
    $this->connection->executeQuery("KILL CONNECTION $id");
    break;
  }
}

注意:注意多个连接,您可能会杀死错误的连接。