Подтвердить что ты не робот

Результаты запроса 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

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

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

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

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

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

4b9b3361

Ответ 1

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

При выполнении этого:

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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

$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 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 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 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 включает в себя:

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

Закрывать и отбрасывать временные таблицы

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

Reset переменные сеанса

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

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

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

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

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

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

Ответ 4

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