Результаты запроса MySQLi: лучший подход, вы закрыли, бесплатно, оба? - PullRequest
35 голосов
/ 10 марта 2010

У меня есть несколько вопросов по поводу использования 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 :: запрос ). И наоборот, в моем обычном справочном тексте по PHP используется free ( PHP и MySQL Web Development , четвертое издание, Уэллинг и Томсон).

Ответы [ 5 ]

53 голосов
/ 10 марта 2010

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

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

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

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

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

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


Должен ли я освободить этот результат раньше назначить новый?

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

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

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


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

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

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

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


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

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

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

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

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

15 голосов
/ 18 августа 2011

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

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

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

0 голосов
/ 07 июня 2018

Спасибо за все ответы, я также хотел бы добавить свой опыт выполнения нескольких запросов MySQL в одном скрипте. Mysqli выдавал ошибки «Команды не синхронизированы» после выполнения запроса после процедур MySql с несколькими запросами. Чтобы решить эту проблему, мне пришлось освободить все открытые наборы результатов, используя решение rizwan-mirza для mysqli_free_result() any mysqli_more_results() https://stackoverflow.com/a/25907704/462781

0 голосов
/ 06 августа 2017

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

$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 e.g.
        // 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

Я также согласен с Паскалем МАРТИНОМ, что было бы неплохо открыть соединение в начале сценария и закрыть его в конце. Я думаю, что пул соединений делает это менее важным, но все же хорошая идея.

0 голосов
/ 10 марта 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...