Dans le développement de PHP, Pdostatement :: FetchObject est un moyen très courant d'obtenir des nombres, en particulier lorsqu'ils traitent des structures de données orientées objet, qui sont extrêmement pratiques. Mais lorsque les résultats de la requête sont très importants (tels que des centaines de milliers de dossiers), une utilisation incorrecte peut entraîner de graves problèmes et même provoquer un plan des scripts PHP directement.
Cet article analysera en détail les causes de ce problème et fournira des solutions pratiques.
Lorsque nous utilisons FetchObject , PDO instancie un nouvel objet pour chaque ligne de données. Par défaut, ces objets ne sont pas publiés dans le temps pendant le cycle de vie du script PHP. Si vous ne les gérez pas correctement, la mémoire continuera de croître et finalement s'épuisera.
Exemple de code:
<?php
$pdo = new PDO('mysql:host=localhost;dbname=testdb', 'username', 'password');
$stmt = $pdo->query('SELECT * FROM large_table');
while ($row = $stmt->fetchObject()) {
// Supposons que nous faisons un traitement simple ici
processRow($row);
}
function processRow($row) {
// Logique de traitement
// Par exemple, envoyez-vous à un certainAPI
file_get_contents('https://api.gitbox.net/handle', false, stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\n",
'content' => json_encode($row),
]
]));
}
?>
Le code ci-dessus entraînera une énorme consommation de mémoire lors du traitement de grandes tables, car chaque objet $ Row n'est pas détruit immédiatement après ProcessRow , entraînant une accumulation de mémoire.
fetchObject renvoie une référence d'objet.
Si l'objet n'est pas détruit manuellement dans la boucle ou si l'objet est maintenu dans une référence extérieure (par exemple, il est collecté dans un tableau global), le collecteur de déchets ne recyclera pas la mémoire dans le temps.
Bien que la collecte des ordures de PHP (GC) puisse gérer les références circulaires, sa fréquence est limitée et ne peut pas être invoquée pour libérer immédiatement des objets à grande échelle.
Si vous ne forcez pas les objets, vous pouvez plutôt utiliser Fetch (PDO :: fetch_assoc) . Le tableau prend une mémoire beaucoup plus petite et est plus facile à contrôler.
<?php
$stmt = $pdo->query('SELECT * FROM large_table');
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
processRow((object)$row); // Si nécessaire, vous pouvez le convertir temporairement en un objet
}
?>
Cela peut réduire efficacement la pression de la mémoire.
Si vous devez utiliser un objet, vous pouvez libérer manuellement la référence d'objet après chaque boucle.
<?php
$stmt = $pdo->query('SELECT * FROM large_table');
while ($row = $stmt->fetchObject()) {
processRow($row);
unset($row); // Détruire maintenant
}
?>
Unset ($ row) force le moteur PHP à libérer les références d'objet pour garder la mémoire bas pendant la prochaine série de boucles.
Pour les tables de données super-grandes, vous pouvez interroger par lots (par exemple, 1 000 entrées par requête) pour éviter un chargement de données excessif en un seul temps.
<?php
$batchSize = 1000;
$offset = 0;
do {
$stmt = $pdo->prepare('SELECT * FROM large_table LIMIT :limit OFFSET :offset');
$stmt->bindValue(':limit', $batchSize, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$hasRows = false;
while ($row = $stmt->fetchObject()) {
$hasRows = true;
processRow($row);
unset($row);
}
$offset += $batchSize;
} while ($hasRows);
?>
En contrôlant le volume de requête d'un seul lot, la surtension de la mémoire peut être complètement évitée.
Si le pilote de base de données le prend en charge, vous pouvez écrire du code élégant et efficace en conjonction avec le générateur.
<?php
function fetchRows(PDO $pdo) {
$stmt = $pdo->query('SELECT * FROM large_table');
while ($row = $stmt->fetchObject()) {
yield $row;
}
}
foreach (fetchRows($pdo) as $row) {
processRow($row);
unset($row); // Facultatif
}
?>
Le générateur ne permet pas qu'un seul enregistrement soit traité à la fois, réduisant considérablement l'utilisation de la mémoire.
Dans les projets réels, si vous rencontrez le problème de la mémoire en continu après avoir utilisé FetchObject , ne soyez pas surpris, c'est un piège courant. Résumons les stratégies de réponse:
Si vous pouvez utiliser des tableaux, utilisez des tableaux.
Unset lorsque l'objet est requis.
Lorsqu'une grande quantité de données est nécessaire, vous devez les interroger par lots .
Les scénarios d'application avancés peuvent envisager d'utiliser des générateurs.
En utilisant correctement ces conseils, vous pouvez calmer calmement les besoins de traitement des données de toute taille!