Michael Keller: Statistik aus MySQL-DB => Geschwindigkeit

Hallo,

Für unsere Seite speichern wir von jedem Aufruf einige Daten in einer Tabelle (MySQL), um danach User-Statistiken zu machen.

Am Anfang ging die auswertung ganz gut... (man musste nur ein paar Sekunden warten..)
Doch inzwischen ist einiges geschehen.. die Tabelle ist jetzt ca. 48 MB gross.

Die Auswertung dauert immer länger... Kann mir jemanden einen Tipp geben, wie ich das ändern könnte?
Kommt es hier auf den Tabellen-Typ an?

Gruss Michael

  1. Hi Michael,

    Die Auswertung dauert immer länger... Kann mir jemanden einen Tipp geben, wie ich das ändern könnte?
    Kommt es hier auf den Tabellen-Typ an?

    nun, sicher kommt es auf das Datenbankdesign im ganzen an. Doch Performanceprobleme rühren meistens eher von dem Script, das - in Deinem Fall die Auswertung - die Arbeit macht. Beispielsweise Schleifen, die viele SQL-Abfragen erfordern, laufen bei wenigen Datensätzen wunderbar und brechen ab einer gewissen Grösse ein.

    Wenn Du den Code zu dem Auswertungsscript bereitstellst, könnte man vielleicht mehr dazu sagen.

    viele Grüsse
    Achim Schrepfer

    1. Hallo Achim

      Wenn Du den Code zu dem Auswertungsscript bereitstellst, könnte man vielleicht mehr dazu sagen.

      bisher ist es sehr rudimentär. Ich stelle diese Frage auch im Hinblick auf die Entwicklung einer besseren Auswertung.

      Gruss Michael

      Hier der Code der bisherigen Auswertung:

      <?
      // 1. Allgemeine Variablen
      $db="xxxstat";
      $sdb="xxxdb";
      $MySQL_Host="xxx";
      $MySQL_User="xxx";
      $MySQL_Passw="xxx";

      // 2. Datenbank Verbindung
       if (! @$cc=mysql_connect("$MySQL_Host","$MySQL_User","$MySQL_Passw")) {
          echo "Die Verbindung zu ",$MySQL_Host," konnte nicht hergestellt werden<br>";
          exit;
       }

      // 3. Allgemeine Funktionen
      function send_sql($db, $sql) {
          if (! $res=mysql_db_query($db, $sql)) {
          echo mysql_error();
          exit;
          }
          return $res;
      }

      // 4. Statistik Funktionen

      // 4.1 verschiedene Besucher
      function verschbes ($anfdatum,$enddatum) {
      global $db;
      $sql="SELECT id,session_id from daten where time>=$anfdatum and time<=$enddatum group by session_id";
      $res=send_sql($db,$sql);
      $verschbes=mysql_num_rows($res);
      return $verschbes;
      }

      // 4.2 Einzelne Seiten ($p)
      function einzseiten ($anfdatum,$enddatum,$p) {
      global $db;
      $sql="SELECT id from daten where time>=$anfdatum and time<=$enddatum and var_p='$p'";
      $res=send_sql($db,$sql);
      $einzseiten=mysql_num_rows($res);
      return $einzseiten;
      }

      // 5. Hauptscript
      echo "<html><head><title>Statistiken für www.ksoe-sphere.ch</title></head><body bgcolor='White'>";
      echo "<h2>Statistiken für www.ksoe-sphere.ch</h2>";
      $verschbes=verschbes("0","99999999999999999999");
      $gesammtbes=$verschbes;
      echo "<h3>Verschiedene Besucher:</h3>";
      echo "<p>Gesammtanzahl verschiedener Besucher: $gesammtbes<br>";
      flush();
      $now=date(U);
      $h=date(G);
      $min=date(i);
      $s=date(s);
      $mon=date(j);
      $monat=$now-($h*3600)-($min*60)-$s-($mon*24*3600)+(24*3600);
      $verschbes=verschbes("$monat","99999999999999999999");
      $monatbes=$verschbes;
      $akmontags=$monatbes/$mon;
      $akmontags=number_format($akmontags,"2",".","'");
      echo "Davon in diesem Monat: $monatbes (Tagesdurchschnitt: $akmontags)<br>";
      flush();
      $now=date(U);
      $h=date(G);
      $min=date(i);
      $s=date(s);
      $heute=$now-($h*3600)-($min*60)-$s;
      $verschbes=verschbes($heute,"99999999999999999999");
      $tagbes=$verschbes;
      flush();
      echo "Davon heute: $tagbes</p>";
      flush();
      echo "<h3>Einzelne Seiten</h3>";
      echo "<p>Anzahl Aufrufe von:<br>";
      $b1=einzseiten ("0","99999999999999999999","root");
      $b2=einzseiten ("0","99999999999999999999","");
      $btot=$b1+$b2;
      echo "root: $btot<br>";
      flush();
      echo "news: ".einzseiten ("0","99999999999999999999","news")."<br>";
      flush();
      echo "kolumne: ".einzseiten ("0","99999999999999999999","kolumne")."<br>";
      flush();
      echo "agenda: ".einzseiten ("0","99999999999999999999","agenda")."<br>";
      flush();
      echo "cards: ".einzseiten ("0","99999999999999999999","cards")."<br>";
      flush();
      $a1=einzseiten ("0","99999999999999999999","forum");
      flush();
      $a2=einzseiten ("0","99999999999999999999","forumdisplay");
      flush();
      $a3=einzseiten ("0","99999999999999999999","threaddisplay");
      flush();
      $a4=einzseiten ("0","99999999999999999999","neu");
      $atot=$a1+$a2+$a3+$a4;
      echo "forum: $atot<br>";
      flush();
      echo "pastevents: ".einzseiten ("0","99999999999999999999","pastevents")."<br>";
      flush();
      echo "gallery: ".einzseiten ("0","99999999999999999999","gallery")."<br>";
      flush();
      echo "anmeldung: ".einzseiten ("0","99999999999999999999","anmeldung")."<br>";
      flush();
      echo "editdata: ".einzseiten ("0","99999999999999999999","editdata")."<br>";
      flush();
      echo "passchange: ".einzseiten ("0","99999999999999999999","passchange")."<br>";
      flush();
      echo "pass_reminder: ".einzseiten ("0","99999999999999999999","pass_reminder")."<br>";
      flush();
      echo "logout: ".einzseiten ("0","99999999999999999999","logout")."<br>";
      flush();
      echo "huusfaescht: ".einzseiten ("0","99999999999999999999","huusfaescht")."<br>";
      flush();
      echo "impressum: ".einzseiten ("0","99999999999999999999","impressum")."<br>";
      flush();
      echo "vote: ".einzseiten ("0","99999999999999999999","vote")."<br>";
      flush();
      echo "gruesse: ".einzseiten ("0","99999999999999999999","gruesse")."<br>";
      flush();
      echo "special: ".einzseiten ("0","99999999999999999999","special")."<br>";

      echo "</body></html>";
      ?>

      1. Hallo Michael,

        // 4.1 verschiedene Besucher
        function verschbes ($anfdatum,$enddatum) {
        global $db;
        $sql="SELECT id,session_id from daten where time>=$anfdatum and time<=$enddatum group by session_id";
        $res=send_sql($db,$sql);
        $verschbes=mysql_num_rows($res);

        Wenn Du es mit "select count(session_id)..." machst, geht es sehr viel schneller, weil MySQL nur eine Zeile zurückliefern muß, nicht ein paar Tausend, wie wahrscheinlich in Deinem Fall.

        // 4.2 Einzelne Seiten ($p)
        function einzseiten ($anfdatum,$enddatum,$p) {
        global $db;
        $sql="SELECT id from daten where time>=$anfdatum and time<=$enddatum and var_p='$p'";

        Der Punkt mit count gilt hier genauso. Außerdem rufst Du die Funktion dann nacheinander ziemlich oft mit einem verschiedenen Parameter für p auf. Besser: nur einmal aufrufen, und den query ungefähr so bauen:
        "select count(*) from daten where time>=$anfdatum and time<=$enddatum group by var_p order by var_p"
        und dann einfach die Anzahl pro Seite der Reihe nach ausgeben.

        Grundsätzlich ist es keine besonders gute Idee, MySQL für eine Zugriffsstatistik zu benutzen, und zwar nicht wegen der Auswertung (die ruft ja nur einer auf), sondern wegen der Tatsache, daß Du bei jedem Zugriff auf eine Seite einen insert auslöst, zumindest nehme ich das mal (und insert sind viel zeitintensiver als selects). Sinnvoller wäre es, die normalen Webserver-Logs zu benutzen, und die Infos einmal im Monat sinnvoll aufbereitet in die MySQL-Datenbank zu schmeißen, z.B. mit einem Cronjob.

        Viele Grüße
        Stephan

        1. Moin,

          ich habe in Deinem Script kein "order by" gefunden. Bei grossen Datenbanken ist es immer sinnvoll nur mit INDEX zu arbeiten. Meine grösste mySQL-Tabelle hat 1,3 GB.
          Mit Index Suchzeit pro Datensatz < 1 sec.
          Ohne Index, je nach Position innerhalb der Tabelle, bis zu 4 min

          Gruss ok

          1. Hallo

            ich habe in Deinem Script kein "order by" gefunden. Bei grossen Datenbanken ist es immer sinnvoll nur mit INDEX zu arbeiten.

            Da werd ich mal ein bisschen nachlesen müssen.. Danke für den Tipp!

            Gruss Michael

        2. Grundsätzlich ist es keine besonders gute Idee, MySQL für eine Zugriffsstatistik zu benutzen, und zwar nicht wegen der Auswertung (die ruft ja nur einer auf), sondern wegen der Tatsache, daß Du bei jedem Zugriff auf eine Seite einen insert auslöst, zumindest nehme ich das mal (und insert sind viel zeitintensiver als selects).

          Sehe ich anders. mySQL ist sehr schnell bei INSERTs (i. Ggs. etwa zu Oracle, die ja Transaktionsmanagement betreibt). Ich mache so ein User-Tracking bei einem Shop mit 75000 PI/Monat (Oracle 8.1.6) und habe keine Performanceprobleme auf der Seite. Mit mySQL sollte es eher noch besser laufen.

          Sinnvoller wäre es, die normalen Webserver-Logs zu benutzen, und die Infos einmal im Monat sinnvoll aufbereitet in die MySQL-Datenbank zu schmeißen, z.B. mit einem Cronjob.

          Aus den Logs geht aber - je nach Application Server - nicht hervor, welcher User den Zugriff ausgelöst hat (IP-Adressen sind ja nicht so wirklich geeignet dafür), d.h. wenn die Session-ID nicht in der URL steht, wird das schwer.
          Außerdem mach dann mal eine Statistik wie "gib mir die Top 10 Seiten nur für eingeloggte User" o.ä.

        3. Wenn Du es mit "select count(session_id)..." machst, geht es sehr viel schneller, weil MySQL nur eine Zeile zurückliefern muß, nicht ein paar Tausend, wie wahrscheinlich in Deinem Fall.

          jep.. da gibts noch einiges an Zeit zu sparen - hoff ich.. werds bald testen.

          "select count(*) from daten where time>=$anfdatum and time<=$enddatum group by var_p order by var_p"
          und dann einfach die Anzahl pro Seite der Reihe nach ausgeben.

          auch hier!

          Grundsätzlich ist es keine besonders gute Idee, MySQL für eine Zugriffsstatistik zu benutzen, und zwar nicht wegen der Auswertung...

          Das gibt mir zu wenig möglichkeiten... User-Tracking nur über IP funkzt nicht, weil sehr viele Besucher von der gleichen IP kommen..

          THX für die Tipps!

          Gruss Michael