2010-05-05 5 views
59

Nur eine kurze Frage.PDO :: fetchAll vs. PDO :: fetch in einer Schleife

Gibt es Leistungsunterschiede zwischen der Verwendung von PDO :: fetchAll() und PDO :: fetch() in einer Schleife (für große Ergebnismengen)?

Ich hole in Objekte einer benutzerdefinierten Klasse, wenn das einen Unterschied macht.

Meine anfängliche ungebildete Annahme war, dass fetchAll möglicherweise schneller ist, weil PDO mehrere Operationen in einer Anweisung durchführen kann, während mysql_query nur eine ausführen kann. Allerdings habe ich wenig Ahnung von inneren Funktionen von PDO und die Dokumentation sagt nichts darüber, und ob fetchAll() einfach eine PHP-seitige Schleife ist, die in ein Array ausgegeben wird.

Irgendwelche Hilfe?

+0

Ich weiß nicht, sehen, aber Ich vermute, es wäre trivial Benchmark zu sein. – Timothy

Antwort

67

Kleiner Benchmark mit 200k zufälligen Datensätzen. Wie erwartet, ist die Methode fetchAll schneller, benötigt aber mehr Speicher.

Result : 
fetchAll : 0.35965991020203s, 100249408b 
fetch : 0.39197015762329s, 440b 

Der Benchmark-Code verwendet:

<?php 
// First benchmark : speed 
$dbh = new PDO('mysql:dbname=testage;dbhost=localhost', 'root', ''); 
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
$sql = 'SELECT * FROM test_table WHERE 1'; 
$stmt = $dbh->query($sql); 
$data = array(); 
$start_all = microtime(true); 
$data = $stmt->fetchAll(); 
$end_all = microtime(true); 

$stmt = $dbh->query($sql); 
$data = array(); 
$start_one = microtime(true); 
while($data = $stmt->fetch()){} 
$end_one = microtime(true); 

// Second benchmark : memory usage 
$stmt = $dbh->query($sql); 
$data = array(); 
$memory_start_all = memory_get_usage(); 
$data = $stmt->fetchAll(); 
$memory_end_all = memory_get_usage(); 

$stmt = $dbh->query($sql); 
$data = array(); 
$memory_end_one = 0; 
$memory_start_one = memory_get_usage(); 
while($data = $stmt->fetch()){ 
    $memory_end_one = max($memory_end_one, memory_get_usage()); 
} 

echo 'Result : <br/> 
fetchAll : ' . ($end_all - $start_all) . 's, ' . ($memory_end_all - $memory_start_all) . 'b<br/> 
fetch : ' . ($end_one - $start_one) . 's, ' . ($memory_end_one - $memory_start_one) . 'b<br/>'; 
+28

Ihr Benchmark ist komplett fehlerhaft! Sie speichern Ihre Daten nicht im $ data-Array im 2. Benchmark (im 'while'). Hast du wirklich gedacht, dass der Unterschied im Gedächtnis so groß ist? Wow, das ist eine ** wirklich schlechte Antwort **! – Rudie

+60

Ja, das tust du nicht. Es ist das Ziel des Benchmarks: Erstens, du machst einen Fetch. Dann arbeitest du an den Daten. Zweitens, du würdest eine Zeile holen, die Arbeit an dieser Zeile machen und dann die nächste Zeile holen. Ein gutes Beispiel wäre, wenn Sie eine Datentabelle anzeigen, müssen Sie alle Ihre Daten speichern, bevor Sie in den Puffer schreiben oder nicht? – Arkh

+1

Entschuldigung für das Nekrobieren, ich verstehe nicht, warum Leute sagen würden, dass dies eine schlechte Benchmark ist. Es gibt keinen Grund, den gesamten Datensatz zu speichern, es sei denn, Sie geben diese Daten an einen Benutzer zurück ... was an erster Stelle einfach nur schlecht ist, verwenden Sie in diesem Fall Paging. Wenn Sie Daten in einer Datenbank massenweise ändern müssen, sollten Sie dies in der Datenbank entweder mit einem Skript oder einer gespeicherten Prozedur, z. temporäre Tabellen – Populus

9

Eine Sache über PHP, die ich gefunden habe, um wahr zu sein fast immer ist, dass eine Funktion, die Sie selbst implementieren wird fast immer langsamer sein als das PHP-Äquivalent. Dies liegt daran, dass, wenn etwas in PHP implementiert ist, es nicht alle Kompilierzeit-Optimierungen, die C hat (in denen PHP geschrieben ist), aufweist und es einen hohen Aufwand an PHP-Funktionsaufrufen gibt.

+0

Es gibt Zeiten, in denen es sich lohnt, PHP nicht zu verwenden. Zum Beispiel das Durchsuchen eines sortierten Arrays (binäre Suche ftw). – Reece45

+2

Ich bin mir nicht sicher, ob ich deine Antwort richtig verstanden habe, aber ich muss nach dem Aufruf noch ein paar Operationen an allen Objekten durchführen, was zweifellos eine weitere foreach-Schleife erfordern würde. Soll ich nur ein Objekt nach dem anderen holen und die Operationen für jedes Objekt ausführen, während es abgerufen wird? –

+0

@ AlReece45 Sie haben zwei völlig verschiedene Funktionen beschrieben. Ich sprach über die Neuimplementierung der Sortierfunktion in PHP vs Verwendung von PHP "sort". @Byron Ich wette, Sie werden feststellen, dass das Holen aller Ergebnisse mit fetchAll() immer noch schneller ist, aber Sie könnten es mit 'microtime (TRUE)' vergleichen, wenn Sie Zweifel haben. –

8

@Arkh

// $data in this case is an array of rows; 

$data = $stmt->fetchAll(); 


// $data in this case is just one row after each loop; 

while($data = $stmt->fetch()){} 


// Try using 

$i = 0; 

while($data[$i++] = $stmt->fetch()){} 

Die Speicher Differenz neglijable werden sollte

+2

@stancu die oberen und unteren Varianten sind effektiv identisch, und die zusätzliche MEM gesehen mit fetch() ist wahrscheinlich ein Artefakt des Overheads der while(). Der Punkt von fetch() besteht darin, eine Zeile zu einer Zeit zu verarbeiten, wobei while() verwendet wird, um dasselbe wie fetchAll (PDO :: FETCH_NUM) zu erreichen, da Sie C-Level-Compiler-Optimierungen im PDO verlieren Modul. – DavidScherer

4

Als Mihai Stancu zu sagen war, ist es fast keinen Unterschied Speicher obwohl fetchAll fetch + while schlägt.

Result : 
fetchAll : 0.160676956177s, 118539304b 
fetch : 0.121752023697s, 118544392b 

bekam ich die Ergebnisse oben, während korrekt ausgeführt wird:

$i = 0; 
while($data[$i++] = $stmt->fetch()){ 
    // 
} 

So ist die fetchAll weniger Speicher verbraucht, sondern holen + während schneller! :)

+6

Schneller? 0.16 ('fetchAll') vs. 0.12 (' fetch') – Joost

+3

Hoppla, ich bin müde .. Bearbeitet. :} – Rihards

+1

Mit erheblich größeren Resultsets würden Sie einen signifikanten Unterschied zwischen PDOStatement :: fetch() und PDOStatement :: fetchALL() sehen. Das Bestimmen, was als "Signifikant Größer" bezeichnet wird, hängt von der Größe jeder Zeile ab. Darüber hinaus verwendet PDOStatement :: Fetch()/fetchAll() standardmäßig den Abrufmodus PDO :: FETCH_BOTH, der die Größe jeder Zeile effektiv verdoppelt. Wenn Sie dies ändern, kann dies die MEM-Verwendung bei großen Ergebnismengen verringern. – DavidScherer

1

Ich weiß, das ist ein altes Thema, aber ich laufe über diese mit der gleichen Frage. Nachdem ich meinen eigenen einfachen "Benchmark" ausgeführt und gelesen habe, was andere hier geschrieben haben, bin ich zu dem Schluss gekommen, dass dies keine exakte Wissenschaft ist und obwohl man sich bemühen sollte, den Qualitäts-, Licht-Code zu schreiben, hat es keinen Sinn, am Anfang zu viel Zeit zu verschwenden von dem Projekt.

Mein Vorschlag ist: Sammeln Sie Daten, die durch den Code ausgeführt wird für eine Weile und dann die Optimierung starten (in der Beta?).

In meinem einfachen Benchmark (nur getestete Ausführungszeit) habe ich Ergebnisse, die zwischen 5% und 50% BEIDE Wege variieren. Ich führe beide Optionen im selben Skript aus, aber wenn ich fetch + führe, war es früher schneller als fetchall und umgekehrt. (Ich weiß, ich hätte sie einfach laufen lassen und ein paar hundert Mal den Median und den Mittelwert erhalten und dann vergleichen sollen, aber ich habe zu Beginn festgestellt, dass es in meinem Fall zu früh ist, damit anzufangen.)

3

Aber sicher, wenn Sie die abgerufenen Daten in einem Array speichern, wird die Speicherauslastung gleich sein?

<?php 
define('DB_HOST', 'localhost'); 
define('DB_USER', 'root'); 
define('DB_PASS', ''); 
// database to use 
define('DB', 'test'); 
try 
{ 
    $dbh = new \PDO('mysql:dbname='. DB .';host='. DB_HOST, DB_USER, DB_PASS); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
    $sql = 'SELECT * FROM users WHERE 1'; 
    $stmt = $dbh->query($sql); 
    $data = array(); 
    $start_all = microtime(true); 
    $data = $stmt->fetchAll(); 
    $end_all = microtime(true); 

    $stmt = $dbh->query($sql); 
    $data = array(); 
    $start_one = microtime(true); 
    while($data = $stmt->fetch()){} 
    $end_one = microtime(true); 

    // Second benchmark : memory usage 
    $stmt = $dbh->query($sql); 
    $data = array(); 
    $memory_start_all = memory_get_usage(); 
    $data = $stmt->fetchAll(); 
    $memory_end_all = memory_get_usage(); 

    $stmt = $dbh->query($sql); 
    $data = array(); 
    $memory_end_one = 0; 
    $memory_start_one = memory_get_usage(); 
    while($data[] = $stmt->fetch()){ 
    $memory_end_one = max($memory_end_one, memory_get_usage()); 
    } 

    echo 'Result : <br/> 
    fetchAll : ' . ($end_all - $start_all) . 's, ' . ($memory_end_all - $memory_start_all) . 'b<br/> 
    fetch : ' . ($end_one - $start_one) . 's, ' . ($memory_end_one - $memory_start_one) . 'b<br/>'; 
} 
catch (PDOException $e) 
{ 
    echo $e->getMessage(); 
} 
?> 

Result : 
fetchAll : 2.6941299438477E-5s, 9824b 
fetch : 1.5974044799805E-5s, 9824b 
4

Alle Benchmarks, über denen der "memory footprint" gemessen wird, sind aus dem einfachen Grund eigentlich falsch.

PDO lädt standardmäßig alle Dinge in den Speicher und es ist egal, ob Sie fetch oder fetchAll verwenden. Um wirklich Vorteile der ungepufferte Abfrage erhalten Sie PDO anweisen sollten ungepufferten Abfragen zu verwenden:

$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

In diesem Fall werden Sie großen Unterschied in dem Speicherbedarf des Skripts

+0

Was ist der Unterschied zwischen der Verwendung von '$ stmt-> fetch()' bei der Verwendung gepufferter Abfragen (der Standard) und der Verwendung von '$ stmt-> fetch()' mit ungepufferten Abfragen ('PDO :: MYSQL_ATTR_USE_BUFFERED_QUERY' Attribut auf' false gesetzt ')? Ich habe gesehen, dass '$ stmt-> fetch()' auch bei Verwendung des Standard-Puffermodus für sehr große Datenmengen funktioniert, während '$ stmt-> fetchAll()' einen Speicherbegrenzungsfehler zurückgeben kann. Also ist '$ stmt-> fetch()' irgendwie 'ungepuffert'? – tonix