php - mysqli_poll如何工作?

标签 php mysql mysqli

mysqli_poll的文档有点稀疏。

在示例中,他们创建了3个相同的数组,其中包含要检查的所有MySQLi连接,但是如果您阅读了参数说明,那根本就不合适。

在我看来,$read是一个数组,用于保存要检查的连接,但是$error$reject应该是未填充的var,如果有错误,该变量将由该函数填充。那是对的吗?

函数返回> = 1时会发生什么?您如何知道哪些连接已准备好“收割”数据? $read也被修改了吗?即减少到实际具有数据的设置连接?

最后,secusec实际上有什么作用吗?如果是这样,该怎么办?我尝试将sec设置为0并将usec设置为1(我认为这意味着0秒+ 1微秒= 1微秒的总等待时间),但是当我运行大型查询时它会暂停一秒钟以上,因此它似乎不会中止或在超时时导致错误。它有什么作用?

最佳答案

TL; DR:您的假设是正确的,但请注意编码。

mysqli_poll是thin convenience wrapper around a socket select,知道 socket_select 的工作原理将使您走很长一段路才能了解此功能。
mysqli_poll仅在基础驱动程序为mysqlnd时可用,因为只有MySQL ND提供了对MySQL服务器的 native 套接字级别的访问。要记住的重要一点是,“套接字级”访问使轮询成为可能,并且为什么理解套接字选择对于理解mysqli_poll的功能和局限性至关重要。

要回答您的问题:

It sounds to me like $read is an array holding the connections to check, but $error and $reject should be unpopulated vars that will be filled by the function if there are errors. Is that correct?



是的,但不是完整的图片。注意mysqli_poll的签名:

int mysqli_poll ( array &$read , array &$error , array &$reject , int $sec [, int $usec ] )



这三个数组都是按引用传递的,这意味着PHP引擎可以修改所有三个数组。在明显的情况下,当$error所请求的任何连接处于错误或连接被拒绝状态时,它将修改$reject$read

但是,当有等待读取的数据时,PHP也会修改$read。这是回答您的问题的关键:

What happens when the function returns >= 1? How do you know which connections have data ready to be "reaped"? Is $read modified too? i.e. reduced to the set connections that actually have data?



是的,这很关键,在文档中并不明显。 $read将被修改为准备读取的连接列表。您将遍历它们并开展业务。但是,必须注意的一点是:如果修改了$read,将轮询置于循环中并再次尝试从中读取会发生什么情况?好吧,您只会从子集中读取内容,这不是您想要的。

在PHP中进行选择时,大多数示例显示的是将源$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;

最后一个问题:

Lastly, do sec and usec actually do anything? If so, what? I tried setting sec to 0 and usec to 1 (I assume that means 0 seconds + 1 microsecond = 1 microsecond total wait time) but it pauses for more than a second when I run a big query, so it doesn't seem to abort or cause an error when it times out. What does it do?



是的,它有效。这些应该表示,代表上限PHP将等待$read中的任何连接上的数据变得可用(但继续读取)。它对您不起作用,因为最短时间为1秒。当您将0设置为秒时,即使您有一个> 0微秒,PHP也会将其解释为“永远等待”。

附带说明,unit tests for mysqli_poll 可能会发光。

更新:我昨晚不在计算机旁进行测试。现在,我已经分享了一些看法。

测试1:长时间运行的查询
$ 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秒钟的简单 sleep 。 mysqli_poll的超时时间为1.5秒。如预期的那样,经过1.5秒后,轮询又返回了。同样,正如预期的那样,没有准备好读取的数据,因此do .. while重新启动。在剩下的半秒后,轮询将返回,表明已准备好读取一个链接。这是预料之中的,因为查询仅需2秒即可解决,并且轮询发现该时间几乎恰好是2秒。

如果将轮询超时更改为半秒,然后重新运行:
// changed this from 1 to 0 --------V
mysqli_poll($read, $error, $reject, 0, 500000);

轮询将在半秒钟后开始,并且循环将按预期运行四次。如果像示例中那样将其更改为1微秒,则它会在1微秒后开始运行。而且,如果将其更改为0秒和0微秒,则它会尽可能快地运行。

因此,当我说0意味着永远等待时,我绝对是错的。

测试2:多个查询,有些错误,有些长时间运行,并且超时

让我们更改脚本,使其具有更多链接,然后重试:
$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:我们正在遍历所有链接,并且要求每个链接都获得 yield 。收割过程将一直等到查询结束。

测试3:长期运行并有针对性的查询:

与测试#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了。

关于php - mysqli_poll如何工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32575987/

相关文章:

php - 在一行中显示具有重复用户 ID 的多条记录

php - php 代码中未处理 Html 搜索栏值

PHP 函数和 MySQL 连接

php - Mysqli 查询不执行

mysql - 如何在数据库更新查询中使用FOR或WHILE?

javascript - 客户端(JS-Browser)和服务器(PHP)通过 Web-Socket 通过 IP 进行通信

php - 需要建议同时在mysql中插入两条记录

php - 在哪里可以获得英语的 UTF-8 字符的完整列表?

php - 如何在mysql中连接两列与连接表

javascript - 表单提交时出现 jquery 错误