Есть ли разница между PDO :: exe c и PDO :: query при использовании PDO :: ATTR_PERSISTENT = true? - PullRequest
5 голосов
/ 29 января 2020

Кажется, есть разница между PDO::exec и PDO::query при наличии PDO::ATTR_PERSISTENT = true. При использовании PDO::exec соединения не будут использоваться повторно, что в итоге приведет к ошибке «ко многим соединениям» по MySQL, так как они не будут закрыты (или повторно использованы).

См., Например, это небольшой фрагмент кода:

<?php

$pdo_attr = [
    PDO::ATTR_PERSISTENT => true,
];

$pdo = new PDO("mysql:host=mysql;dbname=empty", "root", "iamreallysecure", $pdo_attr);

// The following will give a to many connections error after a few browser refreshes (connection limit is set to 10)
var_dump($pdo->exec("SELECT 1"));

// While the following does NOT leave >10 connections open and continues to work
//var_dump($pdo->query("SELECT 1")->fetchAll());

exec оставит "спящие" MySQL запросы в системе (наблюдается с SHOW PROCESSLIST), в то время как query не вызывает это.

Ожидается ли это? В чем разница между exe c и query в этом случае? Или, может быть, это ошибка?

Пример полностью работающего (docker) находится по адресу https://github.com/Mattie112/mysqltest

Ответы [ 2 ]

8 голосов
/ 29 января 2020

Если вы используете exec() для SELECT, значит, вы уже делаете что-то не так. Эта функция (, как указано в руководстве ) не извлекает результаты из базы данных. Он может использоваться только для запросов, которые не дают набора результатов и не имеют ввода переменных. Если запрос дает результат, вам нужно получить этот результат из MySQL, используя то же соединение.

Постоянное соединение не может быть повторно использовано, если оно все еще используется. Это может произойти по многим причинам, одна из которых - несомненный результат. MySQL будет держать соединение открытым, ожидая, пока клиент выполнит некоторые действия с набором результатов. После того как результат полностью получен с сервера MySQL, он может принимать новые запросы.

Это проблема не только с постоянными соединениями, так как при использовании * 1009 может возникнуть проблема с невыполненным набором результатов. * один за другим с обычными соединениями.

$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
];
$pdo = new PDO("mysql:host=localhost;dbname=test;port=3307", "root", "", $options);

// The following will give:
// SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
var_dump($pdo->exec("SELECT 1"));
var_dump($pdo->exec("SELECT 1"));

PDOException: SQLSTATE [HY000]: общая ошибка: 2014 Не удается выполнить запросы, когда другие небуферизованные запросы активны. Рассмотрите возможность использования PDOStatement :: fetchAll (). В качестве альтернативы, если ваш код будет работать только с mysql, вы можете включить буферизацию запросов, установив атрибут PDO :: MYSQL_ATTR_USE_BUFFERED_QUERY. в C: \ wamp64 \ www\test \ index. php в строке 16

Чтобы ответить на ваш вопрос из заголовка: Да. Существует разница между использованием exec() и query() при использовании постоянных соединений, но эта разница также существует, когда не используются постоянные соединения.

tl; dr: не использовать exec() для запросов, которые дают результаты. Вместо этого используйте подготовленные заявления.

0 голосов
/ 30 января 2020

Вам удалось запутать все здесь. Exe c не связан ни с постоянными соединениями, ни с ошибкой слишком большого числа соединений. Очевидно, что это постоянное соединение само несет ответственность за эту ошибку.

Так что всего два практических правила для решения всех ваших настоящих и будущих проблем:

  • не использовать постоянные соединения. Существует гораздо больше вреда, чем пользы
  • не используйте exe c (). Всегда используйте только prepare () / execute () с параметрами, заменяющими действительные переменные в вашем запросе
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...