Результаты запроса MySQLi: лучший подход, вы закрываете, бесплатно, оба?

У меня есть некоторые вопросы об использовании MySQLi , запросов и связанных с ними управления памятью. Код здесь просто для того, чтобы прояснить мои вопросы, поэтому не сбрасывайте его на проверку ошибок и т. Д. Я знаю, что это нужно сделать 🙂

Предположим, у меня есть что-то вроде этого:

@ $db = new mysqli($dbhost, $un, $ps, $dbname); $query = "SELECT field1, field2 ". "FROM table1 ". "WHERE field1={$some_value}"; $results = $db->query($query); while ($result = $results->fetch_object()) { // Do something with the results } $query = "SELECT field1, field2 ". "FROM table2 ". "WHERE field1={$some_value2}"; // question 1 $results = $db->query($query); while ($result = $results->fetch_object()) { // Do something with the second set of results } // Tidy up, question 2 if ($results) { $results->free(); } if ($db) { $db->close(); } // Question 3, a general one 

Итак, основываясь на комментариях в приведенном выше коде, вот мои вопросы:

  1. Когда я определяю результаты второго запроса на $results , что происходит с памятью, связанной с предыдущими результатами? Должен ли я освобождать этот результат перед назначением нового?

  2. Связано с 1, когда я убираю в конце, убирает только последние результаты достаточно?

  3. Когда я пытаюсь очистить результат, должен ли я освобождать его, как указано выше, следует ли закрывать его или и то, и другое?

Я задаю вопрос 3, потому что в документации PHP для mysqli::query есть пример, который использует close, даже если close не является частью mysqli_result (см. Пример 1 в mysqli :: query ). И наоборот, мой обычный справочный текст PHP использует free ( PHP и MySQL Web Development , Fourth Edition, Welling и Thomson).

Когда я определяю результаты второго запроса на $results , что происходит с памятью, связанной с предыдущими результатами?

Когда вы выполните это:

 $results = $db->query($query); 

Если раньше было что-то в $results , этот старый контент больше не может быть доступен, так как ссылки на него отсутствуют.

В таком случае PHP будет отмечать старое содержимое переменной как « не нужно больше » – и он будет удален из памяти, когда PHP нуждается в некоторой памяти.

Это, по крайней мере, верно для общих переменных PHP; в случае результатов SQL-запроса некоторые данные могут храниться в памяти на уровне драйвера, над которыми PHP не имеет большого контроля.

Должен ли я освобождать этот результат перед назначением нового?

Я никогда этого не делаю, но, цитируя страницу руководства mysqli_result::free :

Примечание. Вы всегда должны получать свой результат с помощью mysqli_free_result (), когда ваш объект результата больше не нужен

Вероятно, это не имеет значения для небольшого скрипта … И единственный способ быть уверенным – проверить, используя memory_get_usage до и после вызова этого метода, чтобы увидеть, есть ли разница или нет.

Связано с 1, когда я убираю в конце, убирает только последние результаты достаточно?

Когда скрипты заканчиваются:

  • Соединение с базой данных будет закрыто – это означает, что любая память, которую может использовать драйвер, должна быть освобождена
  • Все переменные, используемые скриптом PHP, будут уничтожены – это означает, что память, которую они используют, должна быть освобождена.

Итак, в конце скрипта, вероятно, нет необходимости освобождать набор результатов.

Когда я пытаюсь очистить результат, должен ли я освобождать его, как указано выше, следует ли закрывать его или и то, и другое?

Если вы закроете соединение с базой данных (используя mysqli::close как вы предложили) , это отключит вас от базы данных.

Это означает, что вам нужно будет повторно подключиться, если вы хотите сделать еще один запрос! Что совсем не хорошо (требуется некоторое время, ресурсы, …)

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

И поскольку « конец скрипта » означает « соединение будет закрыто », даже если вы его не укажете; Я почти никогда не закрываю связь самостоятельно.

Полученные ответы являются хорошими, но я хотел добавить одно очко и уточнить другое.

Во-первых, разъяснение. Что касается использования метода close (), важно отметить, что OP ссылался на метод close () класса mysqli_result, а не на класс mysqli. В классе результатов метод close () является просто псевдонимом метода free (), как показано в документации , в то время как в классе mysqli он закрывает соединение. Таким образом, можно использовать функцию close () для результата вместо free (), если это необходимо.

Во-вторых, дополнительная точка. Как уже указывалось, шаблон исполнения PHP означает, что в конечном итоге все будет очищено за вас, и, следовательно, вам не обязательно беспокоиться о выпуске памяти. Однако, если вы выделяете много объектов результата или выделяете особенно большие объекты результатов (например, извлекаете большой объем данных), тогда вы, вероятно, должны освободить память, когда вы сделаете это, чтобы еще больше предотвратить проблемы по пути выполнения. Это становится особенно важным, так как ваше приложение начинает получать больше трафика, где общий объем памяти, связанный между сеансами, может быстро стать значительным.

Насколько редки они, по-моему, утечки памяти – это кошмар, чтобы найти и исправить. Я ухожу с дороги, чтобы избежать их. Ниже приведен образец, который я использую, на основе кода, который вы указали:

 $db = NULL; try { $dbPool = "p:$dbhost"; // question 3: use pooling $db = new mysqli($dbPool, $un, $ps, $dbname); if ($db->connect_errno) { throw new Exception('' . $db->connect_error . ' ' . $db->connect_errno . "\n" . $un . '@' . $dbhost . ' ' . $dbname); // NOTE: It's commonly considered a security // risk to output connection information eg // host, user and database names. } $query = "SELECT field1, field2 ". "FROM table1 ". "WHERE field1={$some_value}"; $results = NULL; try { if (!$results = $db->query($query)) { throw new Exception($db->error . " " . $db->errno . "\n" . $query); // NOTE: It's commonly considered a security // risk to output SQL ($query). } while ($result = $results->fetch_object()) { // Do something with the results } } catch (Exception $ex) { // log, report, or otherwise handle the error } if ($results) { $results->free(); // question 1: why risk it? } $query = "SELECT field1, field2 ". "FROM table2 ". "WHERE field1={$some_value2}"; $results = NULL; try { if (!$results = $db->query($query)) { throw new Exception($db->error . " " . $db->errno . "\n" . $query); // NOTE: It's commonly considered a security // risk to output SQL ($query). } while ($result = $results->fetch_object()) { // Do something with the second set of results } } catch (Exception $ex) { // log, report, or otherwise handle the error } if ($results) { $results->free(); // question 2: again, why risk it? } } catch (Exception $ex) { // log, report, or otherwise handle the error } if ($db) { $db->close(); } конечные $db = NULL; try { $dbPool = "p:$dbhost"; // question 3: use pooling $db = new mysqli($dbPool, $un, $ps, $dbname); if ($db->connect_errno) { throw new Exception('' . $db->connect_error . ' ' . $db->connect_errno . "\n" . $un . '@' . $dbhost . ' ' . $dbname); // NOTE: It's commonly considered a security // risk to output connection information eg // host, user and database names. } $query = "SELECT field1, field2 ". "FROM table1 ". "WHERE field1={$some_value}"; $results = NULL; try { if (!$results = $db->query($query)) { throw new Exception($db->error . " " . $db->errno . "\n" . $query); // NOTE: It's commonly considered a security // risk to output SQL ($query). } while ($result = $results->fetch_object()) { // Do something with the results } } catch (Exception $ex) { // log, report, or otherwise handle the error } if ($results) { $results->free(); // question 1: why risk it? } $query = "SELECT field1, field2 ". "FROM table2 ". "WHERE field1={$some_value2}"; $results = NULL; try { if (!$results = $db->query($query)) { throw new Exception($db->error . " " . $db->errno . "\n" . $query); // NOTE: It's commonly considered a security // risk to output SQL ($query). } while ($result = $results->fetch_object()) { // Do something with the second set of results } } catch (Exception $ex) { // log, report, or otherwise handle the error } if ($results) { $results->free(); // question 2: again, why risk it? } } catch (Exception $ex) { // log, report, or otherwise handle the error } if ($db) { $db->close(); } конечные $db = NULL; try { $dbPool = "p:$dbhost"; // question 3: use pooling $db = new mysqli($dbPool, $un, $ps, $dbname); if ($db->connect_errno) { throw new Exception('' . $db->connect_error . ' ' . $db->connect_errno . "\n" . $un . '@' . $dbhost . ' ' . $dbname); // NOTE: It's commonly considered a security // risk to output connection information eg // host, user and database names. } $query = "SELECT field1, field2 ". "FROM table1 ". "WHERE field1={$some_value}"; $results = NULL; try { if (!$results = $db->query($query)) { throw new Exception($db->error . " " . $db->errno . "\n" . $query); // NOTE: It's commonly considered a security // risk to output SQL ($query). } while ($result = $results->fetch_object()) { // Do something with the results } } catch (Exception $ex) { // log, report, or otherwise handle the error } if ($results) { $results->free(); // question 1: why risk it? } $query = "SELECT field1, field2 ". "FROM table2 ". "WHERE field1={$some_value2}"; $results = NULL; try { if (!$results = $db->query($query)) { throw new Exception($db->error . " " . $db->errno . "\n" . $query); // NOTE: It's commonly considered a security // risk to output SQL ($query). } while ($result = $results->fetch_object()) { // Do something with the second set of results } } catch (Exception $ex) { // log, report, or otherwise handle the error } if ($results) { $results->free(); // question 2: again, why risk it? } } catch (Exception $ex) { // log, report, or otherwise handle the error } if ($db) { $db->close(); } 

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

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

Откат активных транзакций

Закрытие временных таблиц

Разблокировать столы

Сбросить переменные сеанса

Закрыть подготовленные заявления (всегда происходит с PHP)

Закрыть обработчик

Блокировки замков, полученные с помощью GET_LOCK ()

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

источник: http://php.net/manual/en/mysqli.persistconns.php

Я также согласен с Pascal MARTIN, что неплохо открыть ваше соединение в начале вашего скрипта и закрыть его в конце. Я думаю, что объединение соединений делает это менее важным, но все же хорошей идеей.

Общий способ PHP не закрывать открытый ресурс. Все будет автоматически закрыто на конце скрипта. Единственный случай, когда вам нужно позаботиться о ручном закрытии, – это если у вас длинный тяжелый код для запуска, что не очень часто встречается для PHP.