Как работает mysqli_poll?

Документация по mysqli_poll немного разрежена.

В этом примере они создают 3 идентичных массива, содержащих все соединения MySQLi для проверки, но если вы читаете описания параметров, это выглядит совсем не так.

Мне кажется, что $read – это массив, в котором проверяются соединения, но $error и $reject должны быть незаселенными vars, которые будут заполнены функцией, если есть ошибки. Это верно?

Что происходит, когда функция возвращает> = 1? Как вы узнаете, какие соединения готовы для получения данных? Изменен ли файл $read ? т.е. сводится к установленным соединениям, на самом деле имеющим данные?

И наконец, делаете ли sec и usec самом деле? Если да, то? Я попытался установить sec на 0 и usec на 1 (я предполагаю, что это означает, что 0 секунд + 1 микросекунд = 1 микросекунд общего времени ожидания), но он приостанавливается более чем на секунду, когда я запускаю большой запрос, поэтому он, похоже, не прерывается или вызвать ошибку, когда она истечет. Что оно делает?

Solutions Collecting From Web of "Как работает mysqli_poll?"

TL; DR: Ваши предположения верны, но будьте осторожны при кодировании.


mysqli_poll – это тонкая удобная оболочка вокруг выбора сокета , и зная, как работает функция socket_select вы получите длинный путь к пониманию этой функции.

mysqli_poll доступен только в том случае, если базовым драйвером является mysqlnd , поскольку только MySQL ND обеспечивает собственный доступ на уровне сокета на сервер MySQL. Важно иметь в виду, что доступ «на уровне сокета» делает возможным опрос, и почему понимание выбора сокета имеет решающее значение для понимания функции и ограничений mysqli_poll .

Чтобы ответить на ваши вопросы:

Мне кажется, что $ read – это массив, в котором проверяются соединения, но $ error и $ reject должны быть незаселенными vars, которые будут заполнены функцией, если есть ошибки. Это верно?

Да, но не полная картина. Обратите внимание на подпись 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, так это то, что исходный $read array копируется в новый массив перед тем, как выбрать select. На странице man для mysqli_poll обратите внимание на этот цикл, который «сбрасывает» считываемый массив непосредственно перед вызовом mysqli_poll:

 foreach ($all_links as $link) { $links[] = $errors[] = $reject[] = $link; } 

Это, пожалуй, самый важный момент: каждый из этих массивов, переданных в mysqli_poll будет изменен, когда mysqli_poll завершен: массивы будут обрезаны так, чтобы в результат mysqli_poll только затронутые соединения, поэтому вам нужно каждый раз перезагружать массивы, прежде чем вы вызове mysqli_poll ,

Другой пример можно увидеть в этой заметке PHP на socket_select . Обратите внимание, как $read = $clients; перед выбором?

К вашему последнему вопросу:

И наконец, делаете ли сек и исекс что-то на самом деле? Если да, то? Я попытался установить sec на 0 и usec на 1 (я предполагаю, что это означает, что 0 секунд + 1 микросекунд = 1 микросекунд общего времени ожидания), но он приостанавливается более чем на секунду, когда я запускаю большой запрос, поэтому он, похоже, не прерывается или вызвать ошибку, когда она истечет. Что оно делает?

Да, это работает. Они должны представлять PHP с верхней границей, ожидая, что данные станут доступными для любого из соединений в $read (но прочитайте дальше). Это не сработало для вас, потому что минимальное время составляет 1 секунду. Когда вы устанавливаете 0 на секунду, даже если у вас есть > 0 микросекунды, PHP интерпретирует это как «ждать вечно».

В качестве побочного примечания, модульные тесты для mysqli_poll могут освещаться.


Обновление: я не был рядом с компьютером, чтобы проверить прошлой ночью. Теперь, когда я, у меня есть некоторые замечания, чтобы поделиться.

test 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 

В этом тесте длинный запрос – это простой сон в течение 2 секунд на сервере MySQL. Время 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 микросекунд, он работает так быстро, как это возможно.

Итак, я определенно ошибался, когда я сказал, что это означает, что нужно ждать вечно.

тест 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 : мы итерации по всем ссылкам, и каждый из них просят быть пожинающими. Процесс жатвы ждет, пока запрос не завершится.

test 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 

Намного лучше. Каждый запрос разрешается в удобное время, с заполнением соответствующих массивов. Важное различие в этом примере, по сравнению с предыдущим, заключается в том, что мы перебираем каждый из модифицированных массивов . Это противоречит некоторой документации, которая показывает итерацию по всем ссылкам.

Я открыл документацию об ошибке # 70505 .