mysqli_poll是如何工作的?

mpe*_*pen 6 php mysql mysqli

关于mysqli_poll的文档有点稀疏.

在该示例中,它们创建了3个相同的数组,其中包含要检查的所有MySQLi连接,但是如果您阅读参数描述,则看起来根本不正确.

听起来像是$read一个包含要检查的连接的数组,但是如果有错误$error,$reject应该是未填充的变量将被函数填充.那是对的吗?

当函数返回> = 1时会发生什么?你怎么知道哪些连接有数据准备好"收获"?是$read修改了吗?即减少到实际拥有数据的集合连接?

最后,做什么sec,usec实际上做什么?如果是这样,什么?我尝试设置sec为1 0usec1(我假设这意味着0秒+ 1微秒= 1微秒的总等待时间)但是当我运行一个大查询时它会暂停一秒多,所以它似乎不会中止或导致超时时出错.它有什么作用?

bis*_*hop 10

TL; DR:你的假设是正确的,但要注意你的编码.


mysqli_poll是一个围绕套接字选择精简便利包装器,知道如何socket_select工作将使您在理解此功能方面有很长的路要走.

mysqli_poll仅在底层驱动程序可用时才可用mysqlnd,因为只有MySQL ND提供对MySQL服务器的本机,套接字级访问.要记住的重要一点是"套接字级别"访问是使轮询成为可能的原因,以及为什么理解套接字选择对于理解其功能和局限性至关重要mysqli_poll.

回答你的问题:

听起来像$ read是一个包含要检查的连接的数组,但$ error和$ reject应该是未填充的变量,如果有错误,将由函数填充.那是对的吗?

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

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

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

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

当函数返回> = 1时会发生什么?你怎么知道哪些连接有数据准备好"收获"?$ read也被修改了吗?即减少到实际拥有数据的集合连接?

是的,这是至关重要的,而且在文档中并不明显. $read将被修改为准备读取的连接列表.你会循环使用它们并开展业务.但是一个必要的观点被掩盖:如果$read被修改,如果你将轮询放在一个循环中并试图再次读取它会发生什么?好吧,你只会从一个子集中读取,这不是你想要的.

然而,大多数的例子显示PHP做一个选择时是源$read阵列的复制被select'd之前,一个新的数组.在man手册页中mysqli_poll,注意这个循环在调用mysqli_poll之前"重置"读取数组:

foreach ($all_links as $link) {
    $links[] = $errors[] = $reject[] = $link;
}
Run Code Online (Sandbox Code Playgroud)

这可能是最重要的一点:传入的每个数组mysqli_poll都会在mysqli_poll完成后被修改:数组将被修剪,以便只有受影响的连接在结果中,因此每次调用之前都必须重置数组mysqli_poll.

另一个例子可以在这篇PHP注释中socket_select看到.注意$read = $clients;选择之前怎么样?

至于你的上一个问题:

最后,do sec和usec实际上做了什么吗?如果是这样,什么?我尝试将sec设置为0并将usec设置为1(我假设这意味着0秒+ 1微秒= 1微秒的总等待时间)但是当我运行一个大查询时它暂停了一秒多,所以它似乎没有中止或超时时导致错误.它有什么作用?

是的,它有效.这些应该代表上限PHP将等待数据在任何连接上可用$read(但继续阅读). 它不适合你,因为最短时间是1秒.当你设置0为秒时,即使你有一个> 0微秒,PHP也将其解释为"永远等待".

作为旁注,单元测试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
Run Code Online (Sandbox Code Playgroud)

在此测试中,长时间运行的查询是在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);
Run Code Online (Sandbox Code Playgroud)

民意调查在半秒后开始,循环运行四次,如预期的那样.如果您将其更改为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));
Run Code Online (Sandbox Code Playgroud)

在这个测试中,我希望立即解决两个结果:一个是语法错误,另一个是数据行.我也期望这需要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
Run Code Online (Sandbox Code Playgroud)

它一直等到SLEEP(2)查询结束,违反了超时是等待的上限的断言.发生这种情况的原因是mysqli_reap_async_query:我们正在遍历所有链接,并且每个链接都被要求收获. 收割过程一直等到查询完成.

测试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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在运行它.

$ 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
Run Code Online (Sandbox Code Playgroud)

好多了.每个查询都会在自己的好时间内解析,并填写适当的数组.与早期相比,此示例中的重要区别在于我们遍历每个修改后的数组.这与某些文档相反,后者显示迭代所有链接本身.

我已经打开了文档错误#70505.