【问题标题】:How to stop PHP and SQL execution when user disconnects?用户断开连接时如何停止 PHP 和 SQL 执行?
【发布时间】:2017-05-30 17:16:11
【问题描述】:

我有一个相当繁重的 SQL 搜索查询,需要几分钟才能完成,它是从一个由 ajax 请求调用的 PHP 脚本调用的。

问题是,用户经常因为不耐烦而多次点击搜索按钮,每次都会创建一个新的ajax请求,并执行一个新的PHP脚本。但旧的继续执行,即使连接已被删除。这会导致 SQL 服务器负载始终处于 100% 的 CPU 使用率。

所以我测试了我的理论,即即使关闭浏览器选项卡,脚本也会继续执行。我使用了 2 种类型的查询,即即席查询和存储过程执行,两种方法都做同样的事情,将数字 1-9 插入表中,每个数字之间有 2 秒的延迟。

即席查询:

for($i = 1; $i < 10; $i++){
    $sql = "INSERT INTO t (i) VALUES(?)";
    $res = pdoQuery($sql, array($i));
    if($res === false){
        echo $pdo->getErrorMessage();
        http_response_code(500);
        exit();
    }
    sleep(2);
}

SP 调用:

$sql = "EXEC sp_Slow";
$res = pdoQuery($sql);
if($res === false){
    echo $pdo->getErrorMessage();
    http_response_code(500);
    exit();
}

我是如何测试的:使用触发对每个脚本的 ajax 调用的按钮,我通过单击按钮并立即关闭选项卡来测试它们。然后监控表中的数据。正如我所怀疑的那样,每 2 秒插入一次新数据。 (如果我直接在浏览器中打开脚本并关闭选项卡,而不是通过 ajax 请求它,也会发生这种情况)

我需要一种在用户断开连接时完全终止 PHP 和 SQL 执行的方法,事务并不重要,因为它只是一个选择操作。

【问题讨论】:

  • 也许在第一次点击后禁用按钮并显示其中一个旋转的 twidder 图像以让他们知道正在发生的事情,或者从任何旅游网站的剧本中获取一个页面并将它们重定向到另一个页面在他们等待时说“执行搜索”。
  • 我有兴趣看看这是否可行。但另一种方法是使用会话变量来防止用户多次搜索。
  • 这仅涵盖特定场景(多次单击同一查询),因为用户有时合法地需要修改他们的搜索查询,并且等待 > 1 分钟以完成第一个查询只是糟糕的用户体验
  • @iceman2992 如果是这种情况,您可以考虑只允许每个会话运行一个查询。如果您获取正在执行的查询的 SPID 并将其保存到会话变量中,那么您可以检查该会话变量中的值和kill 与该 SPID 相关的查询。
  • @iceman2992 查看this MSDN question 的答案。基本上只需在脚本开头查询select @@spid 即可获取SPID。将其保存到您的会话变量中。然后执行kill [spid] 以取消该查询。我不知道您使用哪个库来进行查询,或者您的库是否支持 kill(我的 TSQL 工作是在 PHP 之外完成的,所以我不熟悉它的库),但这就是我想要的进入。

标签: php sql-server ajax


【解决方案1】:

您可以使用 php.ini 指令或在运行时使用 ignore_user_abort() 函数更改此行为。

【讨论】:

  • 通过 AJAX 调用执行的 PHP 脚本能否确定用户是否中止了脚本的执行?我相信 OP 的主要关注点是停止执行他的查询。
  • 刚测试,不行,我在脚本顶部加了ignore_user_abort(false);,观察到同样的行为
  • @iceman2992 看这里:stackoverflow.com/questions/7582485/…
  • OP 似乎使用的是 sql server,而不是 mysql。
  • @JonathanKuhn 逻辑是一样的。您基本上从服务器获取进程 ID,然后终止该进程。在这方面,SQL Server 的工作方式与 MySQL 相同。 编辑: 我的错;我应该考虑比第一个答案看得更远。
【解决方案2】:

这就是我所做的,来自@Robbie Toyota 的评论,谢谢!

if(!empty($_SESSION['SearchSPID'])){
    $sql = "KILL " . $_SESSION['SearchSPID'];
    $res = pdoQuery($sql);
    if($res === false){
        exit('Query error: failed to kill existing spid:' . $_SESSION['SearchSPID']);
    }       
    $_SESSION['SearchSPID'] = null;
}

$sql = "SELECT @@spid AS SPID";
$res = pdoQuery($sql);
$spid = $res->row_assoc()['SPID'];

$_SESSION['SearchSPID'] = $spid;

// do long query here

$_SESSION['SearchSPID'] = null;

当然,使用这种方法你必须小心会话文件锁定,如果发生这种情况会使整个事情变得毫无意义,因为这样请求将是顺序的而不是并行的

【讨论】:

  • 感谢您为后代发布您的解决方案。它有效吗?我只是想知道 PDO 是否支持 sql server 的kill。另外,作为@N.B.提到,您应该查看您的搜索查询以加快速度。如果您正确使用索引,并且您没有返回/处理大量数据,则查询不应该花费几分钟。
  • 是的,它确实有效,是的,我也在尝试优化查询。
猜你喜欢
  • 2014-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多