我正在使用PDO,用户应该可以选择停止之前触发的请求。
例如,现在我点击生成报告,但是在请求之后我忘了再选择一个使报告无用的字段。所以我需要取消请求,并创建一个新请求。
基本上,如何取消正在运行的MYSQL查询?
if (!isset($_POST['cancel_request'])) {
//query
}else{
//the user cancel the query
}
我知道我可以使用kill命令和PID进程,但这应该通过PDO运行,我不知道什么是PID。
答案 0 :(得分:6)
这里的主要问题是在生成报告的异步请求和应该停止报告的脚本之间共享PID。
您可以使用以下方式获取PID:
$stmt = $dbh->prepare("SELECT CONNECTION_ID()");
$stmt->execute();
$pid = $stmt->fetchColumn();
您可以使用php-shared-memory之类的东西在脚本之间创建共享变量。如果您没有将Composer用于项目依赖项,则此库具有独立版本(1.5.0,here)。
实施示例:
<?php
if (!include __DIR__ . '/vendor/autoload.php')
{
die('You must set up the project dependencies.');
}
use Fuz\Component\SharedMemory\SharedMemory;
use Fuz\Component\SharedMemory\Storage\StorageFile;
// your intializations here
$storage = new StorageFile("/tmp/shared.{$user_id}.sync");
$shared = new SharedMemory($storage);
if (!isset($_POST['cancel_request']))
{
$stmt = $dbh->prepare("SELECT CONNECTION_ID()");
$stmt->execute();
$pid = $stmt->fetchColumn();
$shared->pid = $pid;
// your long query here
$shared->destroyStorage();
}
else
{
// kills pid
$pid = $shared->pid;
if (!is_null($pid))
{
$dbh->exec("KILL {$pid}");
}
}
答案 1 :(得分:4)
正如其他人所说,您可以登录MySQL服务器,发出SHOW PROCESSLIST
,找到要放弃的查询的编号,然后发出KILL number
命令。
您似乎希望此功能为您的报表设计用户提供自助服务,而不是让他们在您的网络操作中呼叫某人并要求他们这样做,或者教他们使用管理工具。
我以前做过这种事。它分为三个部分。
首先,您需要安排将标记插入用户可能要放弃的查询类型中。例如,如果您的用户正在执行此操作
SELECT cust_id, SUM(orders), COUNT(orders)
FROM big_fat_table
GROUP BY cust_id
您需要更改查询的文本,以便在评论中嵌入标记,例如,如此。
SELECT /*report:user2290084*/ cust_id, SUM(orders), COUNT(orders)
FROM big_fat_table
GROUP BY cust_id
请注意,此标记中包含两个项目:report:
和用户ID。用户标识需要与运行查询的连接无关 - 如果您尝试放弃在其上运行的查询,则该连接将被绑定。标签根本不会影响查询的执行。它只显示在服务器的进程表中。
第二:运行这些查询的PDO代码需要良好且用户友好的异常处理,因为在代码下面查询会变得很常见。您需要对此进行试验,以使此放弃查询功能对您的用户有用。
第三:当您需要放弃user2290084的报告查询时发出此查询
select id from information_schema.processlist where info like '%/*%report:user2290084*/%'
查看进程列表以查找具有相应标记的查询的进程ID。然后发出
kill <<that process id>>
并且您已放弃查询。
在php中,它可能看起来像这样:
$q = 'select id from information_schema.processlist where info like :tag';
$tag = '%/*report:' . $userid . '*/%'
$stmt = $dbh->prepare($q);
$stmt->bindParam(':tag', $tag);
$stmt->execute();
$pids = $stmt->fetchAll(PDO::FETCH_COLUMN, 0);
foreach ($pid in $pids) {
$dbh->query('KILL ' . $pid);
}
请注意,此代码中有一个循环。您可能会为此用户运行多个报表查询,或者没有。
答案 2 :(得分:0)
由于您已经在运行查询并进行第二次转换以取消它,因此您需要了解有关正在运行的查询的一些信息。
http://dev.mysql.com/doc/refman/5.1/en/show-processlist.html
Show processlist
将显示所有查询,找到正确的查询并发出kill
命令(在MySQL中)
http://dev.mysql.com/doc/refman/5.1/en/kill.html
虽然你需要一些信息来匹配。信息框将查询显示为已键入,您可以使用随机数添加注释,并将其存储在会话中。然后将会话随机值与show processlist
结果中的行匹配,这将允许仍然使用查询缓存,并且唯一匹配您需要的内容
虽然您的查询足够长以至于需要这样的事情,但您应该查看为什么查询 慢。
答案 3 :(得分:0)
这是我们在其中一个系统上使用的解决方案。有两个不同的组件(UI和报告处理器)使用数据库进行通信。
UI从用户收集报告参数,然后在报告表中创建一个状态为new
的新条目。报告生成不会自动启动。
用户界面包含所有报告的列表及其当前状态(new
,waiting
,running
,killed
,completed
,{{ 1}})。当客户对报告参数感到满意时,他们会从报告列表中选择报告参数并按下“开始报告”按钮,将报告状态从failed
更改为new
。
每分钟在服务器上运行的cron作业选择waiting
状态的第一个报告,将其状态更改为waiting
并开始从数据库收集其数据。完成后,将状态更改为running
(如果出现问题,则更新为completed
),并使用操作结果更新注释字段(失败时出现错误消息)。
客户可以在UI中查看所有报告的当前状态。如果他们想要停止正在运行的报告,他们会使用“终止”按钮,并且用户界面会将数据库中的报告状态更改为failed
。
报告生成涉及多个阶段和对数据库的大量请求。在这些阶段之间甚至在它们期间,当阶段有许多步骤时,脚本会检查它处理的报告的状态。状态为killed
时,它会继续收集数据并撰写报告。如果状态更改为running
,则表示用户改变了主意;它会停止处理,将报告状态更改为killed
,更新评论(“被用户杀死”),清理并退出。
这只是一个草图。有更多的进程状态以及一些代码可以使cron作业的两个实例处理相同的报告等等,但是这个模型对我们有效期超过四年没有重大问题。
关于运行时间,小报告需要几秒钟才能完成,但更大的报告有时需要2-3小时。虽然可以让5-10秒的报告在之后完成和删除,但要及早删除不需要的报告,否则需要花费几个小时才能完成实施复杂机制的理由。