[MySQL] Forum mit Kategorien, Boards und Posts

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • [MySQL] Forum mit Kategorien, Boards und Posts

    Ich bastle zur Zeit an einem kleinen Forum (PHP), habe allerdings das Problem, dass ein paar Boards mehr als 250 Queries brauchen und die Renderzeit der Seite so in die Höhe hüpft.

    Ich habe folgende Datenbankstrukur:
    - `cats` = die Kategorien; Felder: `id`,`title`,`position`
    - `boards` = die Kategorien; Felder: `id`,`cat_id`,`title`,`desc`,`position`
    - `threads` = die Kategorien; Felder: `id`,`cat_id`,`board_id`,`title`,`date`
    - `posts` = die Kategorien; Felder: `id`,`board_id`,`thread_id`,`text`,`date`,`user_id`

    Im Moment läuft es so, dass mit einer Query die Kategorien ausgelesen werden:

    Quellcode

    1. $sql = "SELECT * FROM `cats` ORDER BY `position` ASC";
    2. $res = mysql_query($sql);
    3. while($row = mysql_fetch_assoc($res)) {
    4. echo "<b>".stripslashes($row['title'])."</b>";
    5. $sql2 = "SELECT * FROM `boards` WHERE `cat_id`= '".$row['id']."' ORDER BY `position` ASC";
    6. $res2 = mysql_query($sql2);
    7. while($row2 = mysql_fetch_assoc($res2)) {
    8. echo "<b>".stripslashes($row2['title'])."</b><br />".stripslashes($row2['desc']);
    9. $sql3 = "SELECT * FROM `posts` WHERE `board_id`= '".$row2['id']."' LIMIT 1";
    10. $res3 = mysql_query($sql3);
    11. while($row3 = mysql_fetch_assoc($res3)) {
    12. echo get_thread_name($row3['thread_id'])."<br />von ".get_username($row3['user_id']);
    13. }
    14. }
    15. }
    Alles anzeigen

    get_thread_name() und get_username() sind einfache Funktionen, die anhand der ID das Feld `title` zurückgeben.

    Ich bin mir sicher, dass es wesentlich einfacher geht, aber ich kenne mich leider mit den Möglichkeiten von MySQL noch nicht allzu gut aus, würde mich also sehr über Tipps freuen! :]
  • Hallo,

    bin gerade auf dem Sprung, von daher eine etwas kurze Antwort zur eigen Recherche:

    1. Ich würde es unter SQL vermeiden alles zu selektieren (*). Selektiere nur das, was du wirklich auch brauchst. Der Grund dafür ist klar. Alles was selektiert wirst, was du nicht brauchst frisst unnötigt Performance.
    2. Du kannst in MySql auch eine Abfrage über mehrere Tabellen machen - Kannst ha mal nach "join" suchen.

    Muss nun los. Ansonsten viel Erfolg weiterhin ;)
  • Im Script an sich werden nur die benötigten Felder selektiert, das war nur zur einfach Demonstration hier, aber danke ;)
    JOINs, da hab ich bereits mit INNER JOIN rumgespielt, das klappte ganz gut. Aber wie verarbeite ich hier das im Endeffekt in PHP - also die Ausgabe? Angenommen ich mach

    Quellcode

    1. $sql = "SELECT `c`.`title` AS `cat_title`, `b`.`title` AS `board_title`, `b`.`id` AS `board_id` FROM `cats` AS `c ` INNER JOIN `boards` AS `b` ON `b`.`cat_id` = `c`.`id` ORDER BY `c`.`position` ASC, ORDER BY `b`.`position` ASC";


    , dann gebe ich es so aus:

    Quellcode

    1. $res = mysql_query($sql);
    2. while($row = mysql_fetch_assoc($res)) {
    3. ???
    4. }


    Und bei den ??? weiß ich nicht, wie ich das ausgeben soll? Hoffe ihr versteht mein Problem gerade :P
  • t_R schrieb:



    Und bei den ??? weiß ich nicht, wie ich das ausgeben soll? Hoffe ihr versteht mein Problem gerade :P


    Ich glaub ich versteh dein Problem nicht :P

    Du hast es doch in dem von dir geposteten Beispiel schon mal ausgegeben, mittels:

    Quellcode

    1. echo $row["SPALTENNAME"];


    Jetzt nimmst du als SPALTENNAME das, was du hinter die "AS" geschrieben hast. (Ich wollt jetzt nicht 'ASSE' schreiben.. könnte falsch gedeutet werden ;) )

    Thimo
  • Naja, dein vermeintliches Problem ist, dass du hier Kategorie- und Forendaten zu jedem Eintrag erhältst.
    Du darfst eben nur die Daten verwenden, die du benötigst.
    Die Abfrage wird dennoch schneller sein, als deine tausenden einzelnen Abfragen.

    Generell würde ich sowas wie Kategorie und Forum aber einfach cachen. Sowas ändert sich selten und wird immer mal verwendet.
  • Ein Cache ist ein Zwischenspeicher um Ergebnisse zu speichern, deren Ermittlung sehr viel Rechenzeit erfordert. Wenn du aufwendige SQL-Statements hast, die sehr häufig abgefragt werden, dann lohnt es sich das Ergebnis zu speichern anstatt die Anfrage immer wieder auszuführen. Das Caching hat aber das Problem, dass es bei Veränderung der Datenbank zu falschen Ergebnissen kommen kann. Deswegen muss man den entsprechenden Cacheeintrag bei Veränderungen resetten. Wenn die Änderungen nicht sofort übernommen werden müssen, kann man dem Cache auch einen timeout oder so etwas geben.

    Ich habe den Verdacht, dass du über deine Tabellen überhaupt keinen Indizes gelegt hast. Wenn dem so ist, dann ist es kein Wunder, dass das so langsam ist.
  • Ich stell auch mal ein paar Fragen, wenn wir schon dabei sind. Helfen dem Threadersteller ja vllt auch, weil er wohl auch nicht so die Ahnung davon hat.


    Also ich kannte das mit dem cachen bei SQL auch noch gar nicht. Aber jetzt mal so gefragt anhand eines Forums.
    Die Foren werden ja wohl nicht häufig geändert. Genauso die Unterforen.
    Aber was ist wenn man die Themen und die Antworten in den cach läd. Hat das überhaupt einen Sinn? Ich mein Themen und Antworten ändern sich bei vielen Foren sekündlich bzw. Minütlich.
    Das heißt wenn die Abfrage nur die Themen und Antworten laden würde die aus dem Cach kommen, werden nur die alten angezeigt, oder man müsste auch in sehr kurzen abständen den Cach aktualiserien.
    Oder versteh ich das ganze Prinzip von der Methode falsch?



    Und das andere, über welche Spalten sollten man denn ein Indize legen? Über die Spalten, die bei der SQL Abfrage häufig genutzt werden?
    Dazu gibt es drei Indizes wenn ich mich nicht irre? Also die Methode "Volltext(Fulltext)" ist wohl für Suchfunktionen mit Match und Aganinst bestimmt?
    Sollte man diesen Spalten dann einen "Index" zuweisen, oder das andere was es noch gibt nennt sich Unique"!?
  • Bzgl. der Cacherei empfehle ich den Einsatz eines Opcode-Caches wie XCache oder eAccelerator, wenn es um wirklich gute Performance geht. Der Opcode hat eine generelle Lebenszeit, z.B. von drei Minuten, nach denen die Inhalte generell neu abgefragt werden. Wird ein neuer Eintrag geschrieben löscht man eben den betreffende Cacheeintrag. In meinem Framework sieht das dann z.B. - in abstrahierter Form - so aus:

    Quellcode

    1. if(!$comments = $this->cache->fetch("news.view.entry.".$id.".comments"))
    2. {
    3. $comment = new Comment;
    4. $comments = $comment->find('all',array('entry_id' => $id));
    5. $this->cache->save("news.view.entry.".$id.".comments", $comments);
    6. }


    $this->cache ist halt nur eine Instanz des Cache-Objekts, das ich auf dem jeweiligen Server verwende - in dem Fall eben Cache_Adapter_Xcache. Wird ein neuer Eintrag geschrieben lösche ich eben mit $this->cache->delete("news.view.entry.".$id.".comments"); den Cache der Kommentare und somit wird er beim nächsten Seitenaufruf neu erstellt. Das reduziert bei aufwendigeren Seiten und vor allem Queries die Ausführungszeit der Seite um Zahlen, die einem den Atem verschlagen :-).

    Nachteil: Der Webhoster (im Idealfall man selbst...) muss den Opcode-Cache in PHP einkompiliert haben bzw. als Modul eingebunden haben. Kaum einer tut das. Aber jede größere und professionelle Webanwendung nutzt Opcode-Caches.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von philippgerard ()

  • Ich speichere im Cache die Ergebnisse der SQL-Abfrage. In meinem Beispiel also quasi (in prozeduralem Code):

    Quellcode

    1. $q = mysql_query("...");
    2. $r = mysql_fetch_assoc($q);
    3. save_in_cache("ein key der immer gleich ist für dieses query - z.B. die id des artikels", $r);
    4. # ...
    5. $r_from_cache = get_from_cache("der gleiche key wie oben");


    Jeder Datenbankadapter wie MySQL oder PgSQL hat auch eigene, interne Query-Caches, die wiederholte Datenbankanfragen beschleunigen. Diese Caches sind jedoch nicht annähernd so effizient und vor allem nicht so steuerbar wie die Opcode-Variante.
  • Ahh cool dankeschön. Das sieht echt gut aus. Könnte ich wohl gebrauchen für meine Anwendung. Da sollte ich mich mal in das Thema einarbeiten. Bzw. XCache oder eAccelerator mir mal näher anschauen.
    Lohnt sich das ganze denn auch noch, wenn man Anwendnungen hat, wo alle paar Sekunden ein UPDATE, INSERT INTO oder DELETE Befehl ausgeführt wird?

    Und sollte man trotzdem dann noch ein Indize auf die Spalten lagen, wie Hafner meinte?

    Sorry für die ganzen fragen, aber ich hab davon null Ahnung.





    Ach wie doof.. Mein Webhoster hat das auch nicht installiert. Schade eigentlich

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Snowflake ()

  • Einen Index solltest du über die Spalten legen, die du im WHERE-Teil oder bei eventuellen JOINs verwendest. Beachte: In Index verkürzt das Lesen von Daten enorm. Beim Schreiben wird jedoch etwas mehr Zeit benötigt (deswegen legt man nicht auf alles einen Index, sondern nur auf die Spalten, bei denen es Sinn macht).

    Edit:
    Einen Index anzulegen ist recht einfach. Ich würde das zuerst einmal probieren. Vielleicht reicht es dir ja dann schon. Caching ist aufwendiger zu implementieren.
    Hier ein Link: sql.1keydata.com/de/sql-create-index.php

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Hafner ()

  • Wow philippgerard da wird man ja echt neidisch. Ich mein so ist das schon ein riesen unterschied. Rechnet man das ganze aber wirklich x 500, staunt man ja echt nicht schlecht, und meine SQL Anwendung ist auch nicht klein.
    Naja ich müsste mich sowieso erstmal einarbeiten wie man das ganze in sein Script einbaut. Und mein Hoster hat das ja leider auch nicht standartmäßig drauf. Leider :( Aber danke für die Tipps.


    Und Hafner. Danke für deinen Tip ich werd das ganze mal Testen. Wenn es wirklich das Lesen enorm verkürzt ist das genau das richtige.
    Ein User fragt auf der Seite zu 80% Daten ab, und zu 20% führt er wirklich Einträge aus. Auch Danke an dich für die Tips und Hilfe :)


    Und @ Themenstarter, ich hoffe ich hab dir dein Thema nicht zweckentfremdet. Hoffe du findest auch hier Lösungen für dein Forum