Bei der Verwendung von SELECT * FROM table WHERE Id IN ( .. )
Abfragen mit mehr als 10000 Schlüsseln, die PDO mit prepare()/execute() verwenden, verschlechtert sich die Leistung um das 10-fache im Vergleich zur Ausführung derselben Abfrage mit mysqli mit vorbereiteten Anweisungen oder PDO ohne vorbereitete Anweisungen.
Weitere seltsame Details:
-
Typischere SELECT-Anweisungen, die nicht die
WHERE Id IN( ..)
Klausel funktioniert auch bei 100K+ Zeilen einwandfrei.SELECT * FROM table WHERE Id
ist zum Beispiel schnell. -
Die Leistungsverschlechterung tritt auf, nachdem prepare()/execute() abgeschlossen ist - sie ist vollständig in
PDOStatement::fetch()
oPDOStatement::fetchAll()
. Die Ausführungszeit der MySQL-Abfrage ist in allen Fällen winzig - es handelt sich nicht um eine Optimierung von MySQL. -
Die Aufteilung der 10K-Abfrage in 10 Abfragen mit 1K Schlüsseln ist leistungsfähig.
-
Die Verwendung von mysql, mysqli mit vorbereiteten Anweisungen oder PDO ohne vorbereitete Anweisungen ist performant.
-
PDO w/prepared benötigt im folgenden Beispiel ~6 Sekunden, während die anderen ~0,5s benötigen.
-
Je mehr Tasten Sie haben, desto schlimmer wird es auf nichtlineare Weise. Versuchen Sie 100K Tasten.
Beispiel-Code:
// $imageIds is an array with 10K keys
$keyCount = count($imageIds);
$keys = implode(', ', array_fill(0, $keyCount, '?'));
$query = "SELECT * FROM images WHERE ImageID IN ({$keys})";
$stmt = $dbh->prepare($query);
$stmt->execute($imageIds);
// until now, it's been fast. fetch() is the slow part
while ($row = $stmt->fetch()) {
$rows[] = $row;
}
1 Stimmen
Wenn dies reproduzierbar ist, müssen Sie wahrscheinlich ein PHP-Profil erstellen, um herauszufinden, warum die Verlangsamung auftritt.
0 Stimmen
Pruebe
PDO::ATTR_EMULATE_PREPARES
oder DeaktivierungPDO::MYSQL_ATTR_USE_BUFFERED_QUERY
. Und beachten Sie, dass sich libmysql- und mysqlnd-Backends unterschiedlich verhalten.0 Stimmen
Habe beides schon ausprobiert, ohne größere Auswirkungen. mysql, mysqli und PDO verwenden alle mysqlnd.
1 Stimmen
Nach Ihrer Beschreibung scheint es sich um einen Nachbearbeitungsfehler zu handeln. Ich würde vermuten, dass die Verlangsamung auf die Handhabung gebundener Parameter zurückzuführen ist. Versuchen Sie
->debugDumpParams()
und suchen Sieis_param=
Werte. Wenn es1
dann durchläuft PDO die Liste und sucht nach gebundenen Variablen, die aktualisiert werden sollen. Vielleicht kann man die Voreinstellung manuell mit->bindValue()
anstelle von->execute(ARRAY)
hilft. Aber ich vermute, dass PDO immer eine Schleife über die gebundene params-Liste machen wird. Bin mir nicht sicher, ob is_param= dafür überhaupt entscheidend ist. (Und zu faul, die pdo_stmt.c zu verstehen)0 Stimmen
Wenn Sie den gleichen Code wie im Beispiel verwenden, um die Abfrage zu erstellen, dann ist Ihr Fehler kurz gesagt, dass Sie, anstatt kommagetrennte Ganzzahlen in MySQLs
IN
geben Sie eine einzelne Zeichenkette mit durch Komma getrennten numerischen Werten an.0 Stimmen
Nun, das Abrufen von 100.000 Zeilen wird sehr viel länger dauern. Netze sind schnell, aber nicht unendlich schnell. Woher wissen Sie, dass die Zeit nicht für die Optimierung/Ausführung benötigt wird? Haben Sie gelesen über dev.mysql.com/doc/refman/5.6/de/ ? MySQL hat versucht, in Version 5.6 neue Funktionen für die Handhabung langer
IN (...)
p0 Stimmen
Sidenote:
IN(...)
ist im Allgemeinen um eine Größenordnung langsamer alsEXISTS(...)
insbesondere bei der Verwendung von Unterabfragen, auch im nativen MySQL-Client, nicht nur in PHP.1 Stimmen
Beachten Sie, dass das Binden der Parameter als String in mysqli mit
$stmt->bind_param(str_repeat('s', count($imageIds)), ...$imageIds);
es no langsamer, als sie als ganze Zahlen zu binden. Und beide mysqli-Methoden benötigen etwa 50% mehr Zeit als eine unvorbereitete Anweisung. Aber die vorbereitete PDO-Anweisung ist wie 50 Mal langsamer (mit 10K Parametern). Es kann also nicht nur daran liegen, dass PDO die Parameter immer als Strings bindet. Auch$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
ändert sich nichts. Mit PDO geht etwas wirklich Seltsames vor sich.1 Stimmen
Haben Sie jemals herausgefunden, was die Ursache dafür ist? Ich habe genau das gleiche Problem.
1 Stimmen
Zugehöriger Fehlerbericht: bugs.php.net/fehler.php?id=53458 - @mario trifft das Problem sehr gut. Die Abrufzeit ist proportional zu der Anzahl der zurückgegebenen Zeilen und der Anzahl der gebundenen Parameter. Bei dieser Art von Abfragen wird ein Problem, das eigentlich linear sein sollte, zu O(n²) . Das bedeutet: 100 mal mehr Parameter => 10000 mal langsamer.