web-dev-qa-db-fra.com

Résultats de la requête MySQLi: meilleure approche, fermez-vous, gratuitement, les deux?

J'ai quelques questions sur l'utilisation de MySQLi , les requêtes et la gestion de la mémoire associée. Le code ici est juste pour clarifier mes questions, alors ne le jetez pas pour vérifier les erreurs, etc. Je sais que cela doit être fait :)

Supposons que j'ai quelque chose comme ça:

@ $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

Donc, basé sur les commentaires dans le code ci-dessus, voici mes questions:

  1. Lorsque j'assigne les résultats de la deuxième requête à $results, qu'advient-il de la mémoire associée aux résultats précédents? Devrais-je libérer ce résultat avant d’attribuer le nouveau?

  2. Par rapport à 1, lorsque je nettoie à la fin, le nettoyage des derniers résultats suffit-il?

  3. Quand j'essaie de nettoyer un résultat, devrais-je le libérer comme ci-dessus, devrais-je le fermer, ou les deux?

Je pose la question 3 car le PHP documentation pour mysqli::query a un exemple qui utilise close, même si close ne fait pas partie de mysqli_result (voir l’exemple 1 dans mysqli :: query). Et au contraire, mon texte de référence PHP normal utilise free (PHP et MySQL Web Development, quatrième édition, Welling et Thomson).

35
Carvell Fenton

Quand j'assigne les résultats du deuxième requête à $results, que se passe-t-il. à la mémoire associée au résultats précédents?

Quand vous exécutez ceci:

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

S'il y avait quelque chose dans $results auparavant, cet ancien contenu ne peut plus être consulté, car il n'y a plus de référence.

Dans un tel cas, PHP marquera l'ancien contenu de la variable comme "n'est plus nécessaire" - et il sera supprimé de la mémoire lorsque PHP aura besoin de mémoire.

Ceci, du moins, est vrai pour les variables générales PHP; Dans le cas des résultats d'une requête SQL, cependant, certaines données peuvent être conservées en mémoire au niveau du pilote - sur lequel PHP n'a pas beaucoup de contrôle.


Devrais-je libérer ce résultat avant le assigner le nouveau?

Je ne fais jamais cela - mais, citant la page de manuel de mysqli_result::free :

Remarque: vous devriez toujours libérer votre résultat avec mysqli_free_result (), quand votre objet de résultat n'est pas nécessaire plus

Cela n'a probablement pas d'importance pour un petit script ... Et le seul moyen d'être sûr serait de tester, en utilisant memory_get_usage avant et après l'appel de cette méthode, pour voir s'il y a une différence ou non.


Relatif à 1, quand je nettoie à la fin, nettoie que le dernier résultats suffisants?

Quand les scripts finissent:

  • La connexion à la base de données sera fermée - ce qui signifie que toute mémoire pouvant être utilisée par le pilote doit être libérée.
  • Toutes les variables utilisées par le script PHP seront détruites, ce qui signifie que la mémoire qu'elles utilisaient doit être libérée.

Donc, à la fin du script, il n’est probablement pas nécessaire de libérer le jeu de résultats.


Quand j'essaie de nettoyer un résultat, devrais-je le libérer comme ci-dessus, devrais-je le fermer, ou les deux?

Si vous fermez la connexion à la base de données (avec mysqli::close comme vous l'avez proposé), cela vous déconnectera de la base de données.

Ce qui signifie que vous devrez vous reconnecter si vous souhaitez effectuer une autre requête! Ce qui n’est pas bon du tout (prend du temps, des ressources, ...)

De manière générale, je ne fermerais pas la connexion à la base de données avant d’être vraiment sûr de ne plus en avoir besoin - ce qui signifie que je ne me déconnecterais pas avant la fin du script.

Et comme "fin du script" signifie "la connexion sera fermée" même si vous ne le spécifiez pas; Je ne ferme presque jamais la connexion moi-même.

52
Pascal MARTIN

Les réponses déjà fournies sont bonnes, mais je voulais ajouter un point et en clarifier un autre.

Tout d'abord, la clarification. En ce qui concerne l'utilisation de la méthode close (), il est important de noter que l'OP faisait référence à la méthode close () de la classe mysqli_result et non à la classe mysqli. Dans la classe de résultats, la méthode close () est simplement un alias de la méthode free (), comme indiqué dans documentation , tandis que dans la classe mysqli, elle ferme la connexion. Ainsi, vous pouvez utiliser close () sur le résultat à la place de free () si vous le souhaitez.

Deuxièmement, le point supplémentaire. Comme cela a déjà été souligné, le modèle d'exécution de PHP signifie que tout sera éventuellement nettoyé derrière vous et que, par conséquent, vous n'avez pas nécessairement à vous soucier de la libération de mémoire. Toutefois, si vous allouez de nombreux objets de résultat, ou si vous allouez des objets de résultat particulièrement volumineux (par exemple, l'extraction d'une grande quantité de données), vous devriez probablement libérer de la mémoire lorsque vous avez terminé pour éviter davantage de problèmes. sur le chemin de l'exécution. Cela devient particulièrement important lorsque votre application commence à générer davantage de trafic, où la quantité totale de mémoire liée au cours des sessions peut rapidement devenir importante.

15
mr. w

Merci pour toutes les réponses, j'aimerais également ajouter mon expérience de l'exécution de plusieurs requêtes MySQL dans un seul script. Mysqli a généré des erreurs «Commandes non synchronisées» après l'exécution d'une requête après une procédure MySql avec plusieurs requêtes. Pour résoudre ce problème, je devais libérer tous les ensembles de résultats ouverts en utilisant la solution de rizwan-mirza pour mysqli_free_result() any mysqli_more_results()https://stackoverflow.com/a/25907704/462781

0
Pete

Aussi rares soient-elles, les fuites de mémoire sont, à mon avis, un cauchemar à trouver et à corriger. Je sors de mon chemin pour les éviter. Voici le modèle que j'utilise, basé sur le code que vous avez fourni:

$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();
}

À mon avis, le regroupement de connexions augmente les risques de fuite de mémoire, mais selon le manuel, les bibliothèques de regroupement de connexions effectuent beaucoup de nettoyage pour vous automatiquement:

La connexion persistante de l'extension mysqli fournit cependant code de traitement de nettoyage intégré. Le nettoyage effectué par mysqli comprend:

Annuler les transactions actives

Fermer et déposer des tables temporaires

Déverrouiller les tables

Réinitialiser les variables de session

Fermer les instructions préparées (ça arrive toujours avec PHP)

Fermer le gestionnaire

Libère les verrous acquis avec GET_LOCK ()

Cela garantit que les connexions persistantes sont à l'état propre sur retour du pool de connexion, avant que le processus client ne les utilise.

source:http://php.net/manual/fr/mysqli.persistconns.php

Je suis également d’accord avec Pascal MARTIN pour dire que c’est une bonne idée d’ouvrir votre connexion au début de votre script et de la fermer à la fin. Je pense que la mise en commun des connexions rend cela moins important, mais reste une bonne idée.

0
Jonathan