我想为 php 中的 set_time_limit() 等 sql 查询设置最大执行时间。我能怎么做 ?
5 回答
如果您使用的是mysql 本机驱动程序(自 php 5.3 起常见)和mysqli扩展,您可以使用异步查询来完成此操作:
<?php
// Heres an example query that will take a long time to execute.
$sql = "
select *
from information_schema.tables t1
join information_schema.tables t2
join information_schema.tables t3
join information_schema.tables t4
join information_schema.tables t5
join information_schema.tables t6
join information_schema.tables t7
join information_schema.tables t8
";
$mysqli = mysqli_connect('localhost', 'root', '');
$mysqli->query($sql, MYSQLI_ASYNC | MYSQLI_USE_RESULT);
$links = $errors = $reject = [];
$links[] = $mysqli;
// wait up to 1.5 seconds
$seconds = 1;
$microseconds = 500000;
$timeStart = microtime(true);
if (mysqli_poll($links, $errors, $reject, $seconds, $microseconds) > 0) {
echo "query finished executing. now we start fetching the data rows over the network...\n";
$result = $mysqli->reap_async_query();
if ($result) {
while ($row = $result->fetch_row()) {
// print_r($row);
if (microtime(true) - $timeStart > 1.5) {
// we exceeded our time limit in the middle of fetching our result set.
echo "timed out while fetching results\n";
var_dump($mysqli->close());
break;
}
}
}
} else {
echo "timed out while waiting for query to execute\n";
// kill the thread to stop the query from continuing to execute on
// the server, because we are abandoning it.
var_dump($mysqli->kill($mysqli->thread_id));
var_dump($mysqli->close());
}
我给mysqli_query的标志完成了重要的事情。它告诉客户端驱动程序启用异步模式,同时强制我们使用更详细的代码,但允许我们使用超时(如果需要,还可以发出并发查询!)。另一个标志告诉客户端不要将整个结果集缓冲到内存中。
默认情况下,php 将其 mysql 客户端库配置为在让您的 php 代码开始访问结果中的行之前,将查询的整个结果集提取到内存中。这可能需要很长时间才能传输大型结果。我们禁用它,否则我们可能会在等待缓冲完成时超时。
请注意,有两个地方我们需要检查是否超过了时间限制:
- 实际查询执行
- 在获取结果(数据)时
您可以在 PDO 和常规 mysql 扩展中完成类似的操作。它们不支持异步查询,因此您无法在查询执行时间上设置超时。但是,它们确实支持无缓冲的结果集,因此您至少可以在获取数据时实现超时。
对于许多查询,mysql 几乎可以立即开始将结果流式传输给您,因此仅无缓冲查询就可以让您在某些查询上有效地实现超时。例如,一个
select * from tbl_with_1billion_rows
可以立即开始流式传输行,但是,
select sum(foo) from tbl_with_1billion_rows
需要处理整个表才能开始将第一行返回给您。后一种情况是异步查询超时将节省您的地方。它还将使您免于普通的旧死锁和其他事情。
ps - 我没有在连接本身上包含任何超时逻辑。
请重写您的查询,例如
select /*+ MAX_EXECUTION_TIME(1000) */ * from table
此语句将在指定时间后终止您的查询
您可以在其他 SO 问题上找到答案:
在您的数据库服务器上每秒运行的 cron 作业,连接并执行以下操作:
- 显示进程列表
- 查找查询时间大于您的最大期望时间的所有连接
- 为每个进程运行 KILL [process id]
pt_kill
有这样的选择。但它是按需的,而不是持续监控的。它执行@Rafa 的建议。但是请参阅--sentinel
有关如何接近的提示cron
。