Bottleneck bei Datenbank-Abfrage finden

HanZ

Aktiver NGBler
Registriert
16 Juli 2013
Beiträge
997
Hallo,

ich habe eine Webseite, die beim Absenden eines Formulars eine Datenbankabfrage macht und das Ergebnis direkt auf der Webseite dann darstellt. Man kann nach bestimmten Kriterien filtern. Bei einer Abfrage nach dem kompletten Inhalt benötigt die Webseite ca. 7 Sekunden, bei einer Abfrage mit nur wenigen Ergebnissen ist das Ergebnis sofort da.
Ich bin bisher davon ausgegangen, dass einfach der Datenbestand zu groß ist (ca 3300 Einträge), die alle mit Einträgen aus einer 60.000 Inhalten umfassenden Tabelle verknüpft werden. Wenn ich aber die SQL-Abfrage direkt in der Konsole ausführe, dauert es nur 0.01 Sekunden

Es gibt 2 Tabelle:
- haupt
- merkmale

In "haupt" sind ca. 3300 Datensätze mit je grob 10 Spalten. Jeder einzelne Eintrag dort wird mit ca. 10 - 30 Datensätzen aus der Tabelle "merkmale" verknüpft (anhand der ID). Die Abfrage dazu lautet ca. so:

Code:
Expand Collapse Copy
SELECT ID, abc, cde, fgh, (...), Group_Concat(merkmale SEPARATOR ', ' ) AS merkmale FROM haupt, merkmale WHERE haupt.ID=merkmale.ID GROUP BY haupt.ID;

Mit PHP frage ich das wie folgt ab:
Code:
Expand Collapse Copy
try
    {
        $db = new PDO(...);
    }
    catch(PDOException $e)
    {
        throw new Exception($e->getMessage());
    }

Die Tatsache, dass die Abfrage auf der Konsole so schnell geht, lässt mich vermuten, dass die lange Wartezeit an was anderem liegt, vermutlich PHP?
Auch direkt im Netzwerk auf dem Server (192..../website) geht es nicht schneller.

Am Ende der Abfrage wird aus dem in PHP gefüllten Array jeder Eintrag (foreach Schleife) mit 'echo' auf die Webseite in eine <table> geschrieben.

Wie gehe ich hier denn vor, um am besten rauszufinden, wieso die Abfrage so lange dauert? Oder hat schon jemand direkt eine Idee?

LG
HanZ
 
Zuletzt bearbeitet:
Habs da momentan nicht so mit. Aber verstehe/interpretiere ich dass richtig, dass beim Einlesen immer alle Datensätze abgefragt werden und nicht nur die neuen?

Dann haste dein Problem. Je mehr, desto Schnecke.
 
  • Thread Starter Thread Starter
  • #3
Wieso schreibst du "einlesen"? Ich frage ja ab. Und wie ich schon geschrieben habe, dauert die Abfrage ja direkt auf der Kommandozeile nur ein Bruchteil einer Sekunde. Ich glaube also nicht, dass das Problem DB-bezogen ist.
 
Na Tabelle liest die ja ein.

War nur ein Gedanke.
 
Ich bin da auch nich so schlau, aber ich würde mir auf Verdacht mal debug-fehler ausgeben lassen. Am Ende is das PHP 7 Sekunden lang mit Fehlern und oder fallbacks beschäftigt.
Klingt komisch, is aber nich auszuschliessen.
 
Hallo @HanZ :
Das klingt für mich danach, als ob wirklich das bauen der Tabelle im HTML das Bottleneck ist.

Wenn die DB Abfrage mit gleichen Parametern in PHPmyAdmin oder von der Kommandozeile nur ein paar Sekunden braucht, liegt die Vermutung nah, dass es bei der Aufbereitung der Daten hapert.

Welche PHP Version hast du den aktuell in Verwendung Client/Server? Und kannst du den Code posten, der die Tabelle aufbaut?
 
Braucht PHP lange oder ist es dein browser, dass dir mal zeiten ausgeben, das du überhaupt weißt was lange braucht.
 
  • Thread Starter Thread Starter
  • #8
Welche PHP Version hast du den aktuell in Verwendung Client/Server? Und kannst du den Code posten, der die Tabelle aufbaut?
PHP 7.4 auf einer Synology mit MariaDB10. Der Code ist recht banal:
Code:
Expand Collapse Copy
if ($select->rowCount() > 0)
    {
        echo ':<table id="table">
        <tr>
            <th>abc</th>
            <th>cde</th>
</tr>';
        foreach ($elements as $elem)
        {
            echo '<tr>
            <td>' . $elem["id"] . '</td>' . 
            '<td>' . $elem["merkmale"] . '</td></tr>';
        }
        echo '<table>';

Das hier ist das zuständige Javascript:
Code:
Expand Collapse Copy
            window.addEventListener("DOMContentLoaded", function() {
                
                document.getElementById("anzeigen").addEventListener("click", function () {
                    
                    const xhr = new XMLHttpRequest();
                    xhr.open("POST", "antwort.php");
                    let daten = new FormData(document.getElementById("form"));
                    xhr.send(daten);
                    xhr.onreadystatechange = function () {
                        
                        if (xhr.readyState == 4 &&
                        xhr.status == 200) {
                            document.getElementById("ausgabe").innerHTML = xhr.responseText;
                        }
                    }
                });
            });



Ich habe mal auf meinen Handy die Abfrage auf der Webseite ausgeführt. Dort dauert es 14 Sekunden. Ich gehe also stark davon aus, dass es der Aufbau der Webseite ist, was meint ihr?
Gibt es da Möglichkeiten, die Zeiten zu reduzieren?
 
Ich habe mal auf meinen Handy die Abfrage auf der Webseite ausgeführt. Dort dauert es 14 Sekunden. Ich gehe also stark davon aus, dass es der Aufbau der Webseite ist, was meint ihr?
Erst mal schauen, ob das wirklich so is. ->Entwicklerkonsole vom Browser->Netzwerktab
Edit: Debug-log dort is auch manchmal ganz hilfreich
 
Über wie viele Einträge reden wir hier, wie viel muss der Browser rendern - evtl reicht eine pagination?
Könntest du vielleicht mal alles zur verfügung stellen? Im JS steht ein "getElementById('anzeigen')" was aber in deinem HTML nicht repräsentiert ist.
time:
Expand Collapse Copy
<?php
    
$start = time();

//.. query

$end = time() - $start;
echo $end;
 
  • Thread Starter Thread Starter
  • #11
Erst mal schauen, ob das wirklich so is. ->Entwicklerkonsole vom Browser->Netzwerktab
In der Entwicklerkonsole wird noch das Absenden des Formulars vermerkt, danach aber nichts mehr. Interessant: Wenn ich das Formular absende und direkt anschließend z.B. die Entwicklerkonsole aufrufe oder das Fenster auf z.B. Vollbild schalten möchte, macht er das erst, wenn die Abfrage zurückkommt. Ein weiteres Indiz, dass der Browser also das Bottleneck ist, da er erst alles parsen muss.


Über wie viele Einträge reden wir hier, wie viel muss der Browser rendern - evtl reicht eine pagination?
Aktuell 3360 Zeilen bei 15 Spalten. Pagination ist vermutlich keine Option, da dafür ja dennoch alles geparsed werden muss oder? Und halt nur die Anzeige anders dargestellt wird.

Könntest du vielleicht mal alles zur verfügung stellen? Im JS steht ein "getElementById('anzeigen')" was aber in deinem HTML nicht repräsentiert ist.
Sorry vergessen, das "anzeigen" ist lediglich der Absenden-Button:
Code:
Expand Collapse Copy
<p class="input">
	<input type="reset" value="Zurücksetzen/Reset">
	<input type="button" value="Anzeigen/Show" id="anzeigen" />
</p>

Beim time-counter kommt nur 0 zurück. Wie vermutet.


Gibt es denn eine elegante Lösung, beim Absenden bis zur Darstellung der Daten einen Ladekreis/-balken o.ä. darzustellen? Die Wartezeit selbst empfinde ich nicht als das Problem, es wird in der Regel auf der Seite nur einmal alles abgefragt, danach wird sehr spezifisch gefiltert.
 
Ich glaube du solltest dich weniger um die optik kümmern (Ladebalken) sondern eher um die Performance ;).

Was sagen denn die Devtools > Netzwerk zur Größe die Transportiert wurde, steht ganz unten (in chrome - basierenden browsern)?

50k einträge ist nicht wenig, aber auch nicht so viel das der browser sich aufhängen sollte. Mit Paginierung meinte ich keine clientseitige paginierung sondern die Verwendung von offset limit in SQL.

Du kannst natürlich einen Ladebalken anzeigen, dann brauchst du halt zum abfragen.
 
In der Entwicklerkonsole wird noch das Absenden des Formulars vermerkt, danach aber nichts mehr. Interessant: Wenn ich das Formular absende und direkt anschließend z.B. die Entwicklerkonsole aufrufe oder das Fenster auf z.B. Vollbild schalten möchte, macht er das erst, wenn die Abfrage zurückkommt. Ein weiteres Indiz, dass der Browser also das Bottleneck ist, da er erst alles parsen muss.
Das Netzwerktab muss zumindest im FF vor irgendeiner Aktion aufsein. Wenn Du keinen 2. Monitor für sowas haben solltest, kann man das auch andocken.
Du kannst auch anders (oder zusätzlich) rausfinden, ob der Browser das Prob ist. Einfach die selbe Seite statisch im Web (und vlt. zum Vergleich auch lokal) speichern, aufrufen und kucken was passiert.

Edit: Mit so nem Ladebalken hast Du garnix geschafft. Du weisst nicht, wo das Prob herkommt und der User weiss nicht, warum man mit DSL überall so bescheuerte Ladebalken sieht.
 
  • Thread Starter Thread Starter
  • #14
Was sagen denn die Devtools > Netzwerk zur Größe die Transportiert wurde, steht ganz unten (in chrome - basierenden browsern)?
So sieht das Dev-Tool nach Abfrage der Daten aus:
Unbenannt.png


50k einträge ist nicht wenig, aber auch nicht so viel das der browser sich aufhängen sollte.
Im Endeffekt sind es nur 3360 Einträge. Die 50k Einträge sind jeweils ein dreistelliger Zahlencoder, der dann in einer Spalte als zusammenhängender String dargestellt wird, also quasi : "123, 456, 789"

Mit Paginierung meinte ich keine clientseitige paginierung sondern die Verwendung von offset limit in SQL.
Also dass nur ein Teil der Daten abgefragt wird? Ungerne, mit der gesamten Abfrage sollen ja auch alle Dateien angezeigt werden.

Du kannst auch anders (oder zusätzlich) rausfinden, ob der Browser das Prob ist. Einfach die selbe Seite statisch im Web (und vlt. zum Vergleich auch lokal) speichern, aufrufen und kucken was passiert.
Guter Hinweis. Wenn ich die Seite als .mhtml/.html abspeichere und sie dann öffne, benötigt es exakt die gleichen 6-7 Sekunden, bis der Ladebalken verschwindet. Scheint wohl also tatsächlich an der Dokumentgröße zu liegen.
 
Versuch mal das hier anstelle des Echos für den Aufbau der Tabelleinhalte in der ForEach-Schleife:
PHP:
Expand Collapse Copy
printf('<tr><td>%d</td><td>%s</td></tr>%s', $elem['id'], $elem['merkmale'], PHP_EOL);

Ich weiß nicht 100% ob das ein Gewinn ist, aber es wäre einen Versuch wert.. :)
 
Gerade bei solchen Datenmengen macht es m.E. durchaus Sinn. mit JSON zu arbeiten und das im Frontend zusammenzubauen (Hallo, Vue).
3/4 der zurückgelieferten Daten sind im Grunde nicht relevant, kosten aber Performance.
 
Was mir noch einfällt, von welcher Datenmenge reden wir hier? Also wie viel MB hat die ausgegebene HTML Datei?
 
  • Thread Starter Thread Starter
  • #18
@theSplit: printf bringt leider nichts, hab sogar eher das Gefühl, dass es noch ein Ticken länger dauert.

Die abgespeicherte .html/.mhtml hat ca. 7MB Dateigröße.


@RedlightX: Hast vermutlich Recht, PHP/JS/SQL ist leider nicht mein Expertengebiet, daher bin ich echt schon froh, dass das inzwischen alles so klappt wie ich es mir vorstelle :)
 
Wenn dein Browser zu lange zum rendern der lokal abgespeicherten Tabelle benötigt, ist es ein clientseitiges Bottleneck -> zuwenig Leistung / oder irgendeine Browser Extension welche das rendern ausbremst. Wenn die Entwicklerkonsole offen ist, bremst diese auch manchmal etwas aus.
Hast du mal verschiedene Browser durch probiert?

Warum kommt pagination für dich nicht in Frage?
Ich würde z.B. auch direkt so ewtas wie einsetzen, über json, wenn du zuviele Daten hast -> dann hättest du u. a. Suche, Pagination etc. gleich alles mit dabei.
 
Zuletzt bearbeitet:
Zurück
Oben