MySQL-Query -> scheinbar kein Streaming aus der Datenbank

Hallo! Ich programmiere seit zirka 4 Jahren an einer Suchmaschine und stehe kurz vor der Fertigstellung, was auch langsam Zeit wird. :wink:

Als ich grad mit meinem Volltextsuche-Table handtiere, der 20 Millionen Einträge hat, ist mir was aufgefallen. Wenn ich ein Suchwort eingebe und Enter drücke kommen die Ergebnisse wie ein Blitz auf den Bildschirm geschossen. Wenn ich aber den gesamten Index um dessen verknüpften Dokumente gruppiert durchlaufen lasse, dauert es verständlicher Weise recht lang. Darauf will ich auch gar nicht hinaus. Sondern, mir ist dabei aufgefallen, dass MySQL die Datensäte scheinbar erstmal in den Speicher lädt und dann ausgibt. Ich dachte immer, dass würde in einer Art streaming aus der DB erfolgen.

Beispiel:

$sql1 = $this-db->query(„select * from db1“);
while($row1 = $sql1->fetch_assoc())
{
  $sql2 = $this-db->query("select * from db2 where id=’$row1[id]’ ");

  while($row2 = $sql2->fetch_assoc())
  {
     $this->db->query("insert into db3 () values () ");
  }
}

SQL1 lädt nicht, wie ich angenommen habe einen DS und übergibt diesen sofort an SQL2, damit dieser einen anderen DS aus der DB holen kann, sondern lädt scheinbar erstmal alle DS für SQL1 in den Speicher und übergibt dann alle Ausgaben an SQL2. Also, eigentlich müsste das völlig danaeben sein, oder?

Wie ich auf diesen Schmarren gekommen bin:

Wenn ich SQL1 mit Limit 100 Starte, werden die DS, die SQL2 verarbeitet mittels SQL3 in einen anderen Table gespeichert. Dass passiert unmittelbar nach dem Programmstart. Wenn ich aber das Limit weglasse und genau genommen 20 Mio. Einträge um 360.000 Dokumente gruppiert durchlaufen lasse, dauert es sehr lange, bis die ersten verarbeiteten DS durch SQL3 in der Datenbank gespeichert werden.

Das wäre so, als würde die Schallplatte erstmal vom Anfang bis Ende durchlaufen, bis der ersten Ton aus den Lautsprächern käme. Also, irgendwie habe ich grad 1000 Fragezeichen im Schädel.

Hoffe, von euch weiß jemand Rat.

Beste Grüße
Thor Duisenberg

Hallo Thor-Dirk,
das Problem ist, wenn du alles der Tabelle selektierst und mit php durchläufst, dass php natürlich erstmal alle Daten handeln muss.
Das bedeutet, dass du hier:
"
$sql1 = $this-db->query(„select * from db1“);
while($row1 = $sql1->fetch_assoc())
{
"
alle x-Datensätze durch den Apache bzw. Webserver mit Ressourcen belegst.

besser ist es gleich einen Query zu schreiben, der alles im MySQL-Server erledigt.
(Quelle:
http://www.w3schools.com/sql/sql_insert_into_select.asp)

das sollte eigentlich mit nur einem Query gehen:
(Achtung, ungetestet)

$sql1 = $this-db->query("
Insert into db3
select
db1.feld1,
db1.feld1,
db1.feld3
from db1
inner join db2
on db1.id=db2.id
");

dabei sollten natürlich die Felder ausgewählt werden, die dann in der db3 landen sollen.

wenn du eine Where-Klausel brauchst, kommt die ans Ende des SQL-Queries

Scheint das du 4 jahre am falschen ende gelesen hast.
Php und Mysql und query und Ergebnis.

Du fragst die db und die gibt dir eine antwort zurück und danach gehts weiter.
Und damit php das ergebnis kennt muss es das auch speichern , da hat die datenbank schon lange pause. Mysql server ungleich php prozess.

Wenn du 1000 zeilen als ergebnis hast wirds auch 1000 mal gemacht ,siehe while schleife.

Danke für deine Erläuterungen. Das MySQL-Beispiel diente nur dem Verständnis und sollte keine praktische Anwendung darstellen. Du schreibst, PHP müsse für alle Aussgaben Ressourcen zur Verfgung stellen. Das leuchtet ein. Trotzdem hake ich nochmal nach. Wenn $sql1 beispielsweise 100.000 DS aus der Datenbank holt, braucht dann dieser Query länger bis er den ersten DS davon an While übergibt, als wenn $sql1 nur 1.000 DS verarbeitet? Verstehst du worauf ich hinaus möchte? Wenn MySQL die DS aus der DB streamt, machte das keinen Unterschied. Wenn $sql1 aber erst die 100.000 DS adressiert, findet in diesem Moment eigentlich kein Streaming mehr statt. Ich werde dazu wohl nochmal ein Experiment starten müssen. Ich wage mal einen Vergleich zu fgets() und file(). Mit fgets kann man beispielsweise ein Dokument zeilenweise auslesen (streamen). file() hingegen lädt die gesamte Datei in den Speicher. Das sollte analog als Vergleich dienen.

Danke für den Hinweis. Zum Glück habe ich nicht nur 4 Jahre gelesen. :wink: Ich weiß, das der Flaschenhals nicht bei MySQL, sondern eher bei PHP zu suchen ist. Darum lasse ich die rechenintensiven Scripte in parallelen Threads ablaufen. Für die Erstellung des Volltext-Indexes starte ich beispielsweise 36 Threads gleichzeitig und realisiere die Abarbeitung damit deutlich schneller, als nur mit einer einzelnen Verarbeitung. Die Suchmaschine ist für mich mitunter auch ein Lernprojekt. Gruß Thor

Deine 1000 Zeilen sind die rückgabe .
Diese werden gespeichert und mittels funktionen die die datenstruktur händeln können Zeile für Zeile ausgegeben , da sie schneller sind als mysql_result .

Wenn speicher festegelegt werden soll, dann wäre das mit LIMIT zu erreichen , da weiss man was als ergebnismenge geantwortet wird.

TIP :
Wenn Sie LIMIT row_count mit ORDER BY benutzen, beendet MySQL die Sortierung, sobald die ersten row_count Datensätze des sortierten Ergebnisses gefunden wurden, statt das gesamte Ergebnis zu sortieren.

Abfrage auf 10.000 :
ausgabe von 1000 zeilen ist schneller als die ausgabe von 10.000 und verbraucht auch mehr speicher im php ,

Hier zum buffern von ergebnisen der Mysql db
http://php.net/manual/de/mysqlinfo.concepts.bufferin…

PHP
http://php.net/manual/en/function.mysql-unbuffered-q…

This extension is deprecated as of PHP 5.5.0, and will be removed in the future. Instead, the MySQLi or PDO_MySQL extension should be used.

damit kriegst du dann eine ungepufferte ausgabe , aber das kann dein mysql server auch sprengen .

Aber bedenke bei deinem while würde das nicht funktionieren ,
da bis zur nächsten abfrage alles blockiert ist.

Unless the full result set was fetched from the server no further queries can be sent over the same connection.

Also besser alles in einen query zu packen und dann lieber das erbenis mit php auswerten.

Es geht also nciht unbuffered einen query zu machen um dann danach eine subabfrage selber zu starten auf der selben verbindung , ob man mehrere verbindungen nutzen kann wäre auszuprobieren . Aber am schnellsten ist es alles mysql machen zu lassen und nur noch ergebnisse abzuholen , dafür gibt es ja auch prozeduren und funktionen , wenns denn ein ganz schwierieger query ist , oder und auch views .

Ja, Meister, das war es! :smile:

Ich habe nun testweise einen Query geschrieben, der im buffering-modus 5 Sekunden arbeitet, bevor eine erste Ausgabe erfolgt:

$sql1 = $this->db->query(„select * from db where [ordentlich Arbeit]“);
while{ 1. Ausgabe nach 5 Sekunden }

$sql2 = $this->db->query(„select * from db where [ordentlich Arbeit]“, MYSQLI_USE_RESULT);
while{ 1. Ausgabe ohne Verzögerung }

Also, 1000 Dank für den Tipp! :smile:

kleine Korrektur zu meinem Query oben:

$sql1 = $this-db->query("
Insert into db3 (feld1,feld2,feld3)
select
db1.feld1,
db1.feld1,
db1.feld3
from db1
inner join db2
on db1.id=db2.id
");