mysqli_poll上的文档有点稀疏。
在该示例中,他们创建了3个相同的数组,其中包含要检查的所有MySQLi连接,但是如果您阅读参数说明,那么根本看起来并不正确。
听起来像$read
是一个包含要检查的连接的数组,但是$error
和$reject
应该是未填充的变量,如果有错误将由函数填充。这是对的吗?
当函数返回> = 1时会发生什么?您如何知道哪些连接已准备好数据"收获"? $read
也被修改了吗?即减少到实际拥有数据的设定连接?
最后,sec
和usec
实际上做了什么吗?如果是这样,什么?我尝试将sec
设置为0
并将usec
设置为1(我假设这意味着0秒+ 1微秒= 1微秒的总等待时间)但是当我运行时它会暂停一秒多一个大的查询,所以当它超时时它似乎不会中止或导致错误。它做了什么?
答案 0 :(得分:10)
TL; DR:您的假设是正确的,但请注意您的编码。
mysqli_poll是一个thin convenience wrapper around a socket select,了解socket_select
如何运作会让您在理解此功能方面有很长的路要走。
mysqli_poll
仅在底层驱动程序为mysqlnd
时可用,因为只有MySQL ND提供对MySQL服务器的本机,套接字级访问。要记住的重要一点是" socket-level"访问是使轮询成为可能的原因,以及为什么理解套接字选择对于理解mysqli_poll
的功能和限制至关重要。
回答你的问题:
听起来像$ read是一个包含要检查的连接的数组,但$ error和$ reject应该是未填充的变量,如果有错误,将由函数填充。这是对的吗?
是的,但不是完整的图片。请注意mysqli_poll
:
int mysqli_poll(array& $ read,array& $ error,array& $ reject,int $ sec [,int $ usec])
所有三个数组都是传递引用,这意味着PHP引擎能够修改所有三个数组。在明显的情况下,当来自$error
的任何请求的连接处于错误或连接被拒绝状态时,它会修改$reject
和$read
。
但是当有数据等待读取时,PHP也会修改$read
。这是回答问题的关键:
当函数返回> = 1时会发生什么?你怎么知道哪些连接准备好数据"收获"? $ read也被修改了吗?即减少到实际拥有数据的设定连接?
是的,这是至关重要的,而且在文档中并不明显。 $read
将被修改为准备读取的连接列表。您将遍历它们并开展业务。但是一个必要的观点被掩盖:如果$read
被修改,如果你将轮询放在循环中并试图再次读取它会发生什么?好吧,你只会从一个子集中读取,这不是你想要的。
大多数示例显示在PHP中执行select时,源$read
数组在被选择之前复制到新数组。在mysqli_poll
的手册页中,请注意此循环"重置"在调用mysqli_poll之前读取数组:
foreach ($all_links as $link) {
$links[] = $errors[] = $reject[] = $link;
}
这可能是最重要的一点:当mysqli_poll
完成时,传入mysqli_poll
的每个数组都会被修改:数组将被修剪,以便只有受影响的连接在结果中,所以你每次调用mysqli_poll
之前都必须重置数组。
另一个例子见this PHP note on socket_select
。请注意在选择之前如何$read = $clients;
?
到你的上一个问题:
最后,do sec和usec实际上做了什么吗?如果是这样,什么?我尝试将sec设置为0并将usec设置为1(我假设这意味着0秒+ 1微秒= 1微秒的总等待时间)但是当我运行一个大查询时它暂停了一秒多,所以它似乎没有在超时时中止或导致错误。它做了什么?
是的,它有效。这些应该表示上限PHP将等待数据在$read
中的任何连接上可用(但继续阅读)。 它不适合你,因为最小时间是1秒。当您将 0
设置为秒时,即使您有> 0
微秒,PHP也会将其解释为"永远等待"。
作为旁注,unit tests for mysqli_poll
可能很有启发性。
更新:我昨晚没有在电脑前测试。现在,我已经有了一些观察要分享。
$ cat mysqli_poll_test
$link = mysqli_connect(...);
$sql = 'SELECT SLEEP(2), 1';
mysqli_query($link, $sql, MYSQLI_ASYNC);
$links = array ($link);
$begin = microtime(true);
$i = 0;
do {
printf("start i=%d @ T+%.f\n", $i, (microtime(true)-$begin));
$read = $error = $reject = $links;
mysqli_poll($read, $error, $reject, 1, 500000);
printf(
"finish i=%d, count(read, error, reject)=(%d, %d, %d) @ T+%f\n\n",
$i++, count($read), count($error), count($reject), (microtime(true)-$begin)
);
} while (count($links) !== count($read) + count($error) + count($reject));
$ php mysqli_poll_test
start i=0 @ T+0.000012
finish i=0, count(read, error, reject)=(0, 0, 0) @ T+1.501807
start i=1 @ T+1.501955
finish i=1, count(read, error, reject)=(1, 0, 0) @ T+2.001353
在此测试中,长时间运行的查询在MySQL服务器上进行2秒钟的简单休眠。 mysqli_poll
的超时时间为1.5秒。正如预期的那样,经过1.5秒后,轮询将返回。同样如预期的那样,没有数据可供读取,因此do .. while
重新启动。在剩余的半秒之后,轮询返回指示一个链接准备好读取。这是预期的,因为查询只需2秒即可完成,并且查看非常接近两秒钟。
如果您将轮询超时更改为半秒并重新运行:
// changed this from 1 to 0 --------V
mysqli_poll($read, $error, $reject, 0, 500000);
民意调查在半秒后开始,循环运行四次,如预期的那样。如果您将其更改为1微秒,就像在您的示例中一样,它会在1微秒后启动。如果将其更改为0秒和0微秒,则会尽可能快地运行。
所以,当我说0
意味着永远等待时,我肯定错误。
让我们更改我们的脚本以获得更多链接,然后再试一次:
$link0 = mysqli_connect(...);
$link1 = mysqli_connect(...);
$link2 = mysqli_connect(...);
$sql0 = 'SELECT SLEEP(2) AS wait, 1 AS num';
$sql1 = 'SELECT foo FROM';
$sql2 = 'SELECT 2 AS num';
mysqli_query($link0, $sql0, MYSQLI_ASYNC);
mysqli_query($link1, $sql1, MYSQLI_ASYNC);
mysqli_query($link2, $sql2, MYSQLI_ASYNC);
$links = array ($link0, $link1, $link2);
$begin = microtime(true);
$i = 0;
do {
printf("start i=%d @ T+%.f\n", $i, (microtime(true)-$begin));
$read = $error = $reject = $links;
$count = mysqli_poll($read, $error, $reject, 1, 500000);
if (0 < $count) {
foreach ($links as $j => $link) {
$result = mysqli_reap_async_query($link);
if (is_object($result)) {
printf("link #%d, row=%s\n", $j, json_encode($result->fetch_assoc()));
mysqli_free_result($result);
} else if (false !== $result) {
printf("link #%d, output=%s\n", $j, $link);
} else {
printf("link #%d, error=%s\n", $j, mysqli_error($link));
}
}
}
printf(
"finish i=%d, count(read, error, reject)=(%d, %d, %d) @ T+%f\n\n",
$i++, count($read), count($error), count($reject), (microtime(true)-$begin)
);
} while (count($links) !== count($read) + count($error) + count($reject));
在此测试中,我希望立即解决两个结果:一个是语法错误,另一个是数据行。我还希望这需要1.5秒,因为暂停2秒的查询在超时到期之前不会解决。情况似乎并非如此:
start i=0 @ T+0.000002
link #0, row={"wait":"0","num":"1"}
link #1, error=You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
link #2, row={"num":"2"}
finish i=0, count(read, error, reject)=(1, 0, 0) @ T+2.001756
start i=1 @ T+2.001827
finish i=1, count(read, error, reject)=(0, 0, 3) @ T+3.503024
等待SLEEP(2)
查询解析,违反了超时是等待的上限的断言。发生这种情况的原因是mysqli_reap_async_query
:我们在所有链接上进行迭代,并且要求获得每个链接。 收割过程一直等到查询完成。
与测试#2相同,但这一次让我们对我们收获的东西保持清醒。
$ cat mysqli_poll.php
<?php
$link0 = mysqli_connect(...);
$link1 = mysqli_connect(...);
$link2 = mysqli_connect(...);
$sql0 = 'SELECT SLEEP(2) AS wait, 1 AS num';
$sql1 = 'SELECT foo FROM';
$sql2 = 'SELECT 2 AS num';
mysqli_query($link0, $sql0, MYSQLI_ASYNC);
mysqli_query($link1, $sql1, MYSQLI_ASYNC);
mysqli_query($link2, $sql2, MYSQLI_ASYNC);
$links = array ($link0, $link1, $link2);
$begin = microtime(true);
$i = 0;
do {
printf("start i=%d @ T+%.f\n", $i, (microtime(true)-$begin));
$read = $error = $reject = $links;
$count = mysqli_poll($read, $error, $reject, 1, 500000);
printf(
"check i=%d, count(read, error, reject)=(%d, %d, %d) @ T+%f\n",
$i, count($read), count($error), count($reject), (microtime(true)-$begin)
);
if (0 < $count) {
reap('read', $read);
reap('error', $error);
reap('reject', $reject);
} else {
printf("timeout, no results\n");
}
printf("finish i=%d\n\n", $i++);
} while (count($links) !== count($read) + count($error) + count($reject));
function reap($label, array $links) {
foreach ($links as $link) {
$result = mysqli_reap_async_query($link);
if (is_object($result)) {
printf("%s, row=%s\n", $label, json_encode($result->fetch_assoc()));
mysqli_free_result($result);
} else if (false !== $result) {
printf("%s, output=%s\n", $label, $link);
} else {
printf("%s, error=%s\n", $label, mysqli_error($link));
}
}
}
现在运行它。
$ php mysqli_poll.php
start i=0 @ T+0.000003
check i=0, count(read, error, reject)=(1, 0, 0) @ T+0.001007
read, error=You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
finish i=0
start i=1 @ T+0.001256
check i=1, count(read, error, reject)=(1, 0, 1) @ T+0.001327
read, row={"num":"2"}
reject, error=You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
finish i=1
start i=2 @ T+0.001627
check i=2, count(read, error, reject)=(0, 0, 2) @ T+1.503261
timeout, no results
finish i=2
start i=3 @ T+1.503564
check i=3, count(read, error, reject)=(1, 0, 2) @ T+2.001390
read, row={"wait":"0","num":"1"}
reject, error=You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
reject, error=
finish i=3
好多了。每个查询都会在自己的好时间内解析,并填写适当的数组。与早期相比,此示例中的重要区别是我们遍历每个修改后的数组。这与某些文档相反,后者显示迭代所有链接本身。
我已经打开了documentation bug #70505。