In der PHP-Entwicklung ist Pdostatement :: FetchObject ein sehr häufiger Weg, um Zahlen zu erhalten, insbesondere wenn es um objektorientierte Datenstrukturen geht, die äußerst bequem sind. Wenn die Abfragergebnisse jedoch sehr groß sind (z. B. Hunderttausende von Aufzeichnungen), kann eine falsche Verwendung zu schwerwiegenden Problemen führen und sogar dazu führen, dass PHP -Skripte direkt abgestürzt werden.
Dieser Artikel wird die Ursachen dieses Problems im Detail analysieren und praktische Lösungen bereitstellen.
Wenn wir FetchObject verwenden, instanziiert PDO für jede Datenzeile ein neues Objekt. Standardmäßig werden diese Objekte während des Php -Skriptlebenszyklus nicht rechtzeitig freigegeben. Wenn Sie sie nicht richtig behandeln, wächst der Speicher weiter und läuft schließlich ab.
Beispielcode:
<?php
$pdo = new PDO('mysql:host=localhost;dbname=testdb', 'username', 'password');
$stmt = $pdo->query('SELECT * FROM large_table');
while ($row = $stmt->fetchObject()) {
// Angenommen, wir verarbeiten hier eine einfache Verarbeitung
processRow($row);
}
function processRow($row) {
// Verarbeitungslogik
// Zum Beispiel an eine bestimmte SendeAPI
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),
]
]));
}
?>
Der obige Code verursacht einen enormen Speicherverbrauch bei der Verarbeitung großer Tabellen, da jedes $ row -Objekt nicht unmittelbar nach dem Prozessrow zerstört wird, was zu einer Speicherakkumulation führt.
FetchObject gibt eine Objektreferenz zurück.
Wenn das Objekt in der Schleife nicht manuell zerstört wird oder das Objekt in einer externen Referenz gehalten wird (zum Beispiel in ein globales Array gesammelt wird), recycelt der Müllkollektor den Speicher nicht rechtzeitig.
Obwohl die Müllsammlung von PHP (GC) kreisförmige Referenzen bewältigen kann, ist seine Frequenz begrenzt und kann nicht darauf angewiesen werden, dass sie große Objekte sofort freigeben.
Wenn Sie keine Objekte zwingen, können Sie stattdessen Fetch (pdo :: fetch_assoc) verwenden. Das Array nimmt viel kleinerer Speicher ein und ist leichter zu steuern.
<?php
$stmt = $pdo->query('SELECT * FROM large_table');
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
processRow((object)$row); // Bei Bedarf können Sie es vorübergehend in ein Objekt konvertieren
}
?>
Dies kann den Speicherdruck effektiv verringern.
Wenn Sie ein Objekt verwenden müssen, können Sie die Objektreferenz nach jeder Schleife manuell freigeben .
<?php
$stmt = $pdo->query('SELECT * FROM large_table');
while ($row = $stmt->fetchObject()) {
processRow($row);
unset($row); // Zerstöre jetzt
}
?>
Uneingestellter ($ row) zwingt die PHP -Engine, Objekte zu verweisen, um in der nächsten Runde der Schleifen den Speicher niedrig zu halten.
Für super große Datentabellen können Sie Stapel (z. B. 1.000 Einträge pro Abfrage) abfragen, um übermäßige Datenbelastungen in einem Zeitpunkt zu vermeiden.
<?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);
?>
Durch die Steuerung des Abfragebatmens einer einzelnen Stapel kann der Speicheranlauf vollständig vermieden werden.
Wenn der Datenbanktreiber dies unterstützt, können Sie sowohl elegante als auch effizientes Code in Verbindung mit dem Generator schreiben.
<?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); // Optional
}
?>
Mit dem Generator kann nur ein Datensatz gleichzeitig verarbeitet werden und die Speicherverwendung erheblich reduziert.
Wenn Sie in den tatsächlichen Projekten auf das Problem der kontinuierlichen Erhebung des Gedächtnisses nach der Verwendung von FetchObject stoßen, wundern Sie sich nicht, dies ist eine gemeinsame Falle. Fassen wir die Antwortstrategien zusammen:
Wenn Sie Arrays verwenden können, verwenden Sie Arrays.
Nicht festgelegt, wenn das Objekt erforderlich ist.
Wenn eine große Menge an Daten benötigt wird, müssen Sie sie in Chargen abfragen .
Erweiterte Anwendungsszenarien können in Betracht ziehen, Generatoren zu verwenden.
Wenn Sie diese Tipps korrekt verwenden, können Sie sich ruhig mit den Datenverarbeitungsanforderungen jeder Größe befassen!