Eddie: Caching umfangreicher(!) Datenbankabfragen

Hallo allerseits,

meine User werden in Zukunft auf eine statische(!) Datenbank zugreifen, und zwar sehr oft (pro User mit Sicherheit mehr als 10 Zugriffe pro Minute). Es wird ein bisschen so sein wie Google Suggest: pro eingegebener Buchstabe eine Abfrage.

Die Haupttabelle selbst hat 6.000.000 Einträge, wird mit einigen weiteren Tabellen verknüpft und letztlich muss das Ergebnis (mit oft mehr also 10.000 Einträgen) auch noch sortiert werden um die relevantesten Einträge zu finden. Sprich: langsam. Zumindest nach allen bisherigen Tests. Gibt der User bspw. nur einen Buchstaben ein, sind in der Haupttabelle gleich mal (6.000.000 / 26 Buchstaben) Einträge betroffen.

Also muss ich das Ganze irgendwie optimieren. Anbieten wuerde sich dabei etwa eine konsistente Speicherung der Ergebnisse als Datei. Sucht dann jemand nach "Gnarf", wird erstmal geguckt, ob es nicht bereits eine Datei "Gnarf.xml" gibt. Wenn nicht, dann wird gesucht und die Datei erzeugt.

Ihr koennt euch ja aber vorstellen (Taschenrechner!) wie umfangreich dieses Caching ausfallen würde.

Würdet ihr das auch so machen?
Und vor allem: was müsste ich bedenken? Was sind die Fallstricke?

Danke für eure Hilfe,
Eddie

--
Old men and far travelers may lie with authority.
  1. Moin!

    Also muss ich das Ganze irgendwie optimieren. Anbieten wuerde sich dabei etwa eine konsistente Speicherung der Ergebnisse als Datei. Sucht dann jemand nach "Gnarf", wird erstmal geguckt, ob es nicht bereits eine Datei "Gnarf.xml" gibt. Wenn nicht, dann wird gesucht und die Datei erzeugt.

    Dateizugriffe und insbesondere das Suchen nach existierenden Dateien ist auch zeitaufwendig.

    Wenn du 6 Millionen Einträge in der DB hast, wirst du über kurz oder lang auch 6 Millionen Dateien mit Cacheergebnissen haben.

    Das ist also kein Performanceschub, wenn du die eine Bremse durch eine andere Bremse ersetzt.

    Würdet ihr das auch so machen?

    Nein.

    Und vor allem: was müsste ich bedenken? Was sind die Fallstricke?

    Bedenken und analysieren mußt du, wobei bei deiner Aktion am meisten Zeit verloren geht. Stelle DAS fest, und du hast den Ansatzpunkt für eine Optimierung gefunden.

    - Sven Rautenberg

    --
    "Love your nation - respect the others."
    1. Hallo Sven,

      sorry fuer die lange Antwortzeit, eigentlich sollte der Fragesteller ja doch etwas schneller reagieren :-/ Habe mich aber die letzten Tage doch mit einem ganz anderen Teil meines Projekts beschäftigt und das Cache-Thema etwas vernachlässigt :-(

      Bedenken und analysieren mußt du, wobei bei deiner Aktion am meisten Zeit verloren geht. Stelle DAS fest, und du hast den Ansatzpunkt für eine Optimierung gefunden.

      Also mal angenommen, ich finde die schnellste Datenbankabfrage, dann besteht jede Anfrage (ohne Caching) grob aus folgenden Teilen:
         - PHP-Script ausführen
         - DB-Anfrage ausführen
         - Ergebnis aufbereiten
         - Ergebnis ausliefern

      Mit Datei-Caching würde es folgendermaßen aussehen:
         - direkter HTTP-Zugriff auf die XML-Datei (Dateiname mittels Ajax)
             falls vorhanden:
             - ausliefern, fertig

      falls nicht vorhanden:
             - Scriptaufruf mittels Apache mod_rewrite/404
             - obige Schritte
             - XML-Datei cachen

      Wenn du 6 Millionen Einträge in der DB hast, wirst du über kurz oder lang auch 6 Millionen Dateien mit Cacheergebnissen haben.

      Ok, da gäbe es ja die Möglichkeit, nur die tatsächlich angefragten Suchen zu cachen. Und über das Attribut "letzter Zugriff" jeder Cache-Datei müsste es auch möglich sein, selten genutzte Abfragen regelmäßig zu löschen. Dann wären das noch ein paar Zehntausend XML-Dateien: wäre es dann nicht (unter Umgehung des PHP-Scripts) tatsächlich eine sinnvolle Optimierung?

      Gruss,
      Eddie

      --
      Old men and far travelers may lie with authority.
  2. Hallo Eddie,

    Um solche Anfragen mit Teilwörtern zu beantworten, eignen sich typischerweise Tries ganz gut.
    Mit einer Tiefe von 5 kannst Du schon (fast) alle Einträge einzeln ansprechen. Die Ergebnisse für Prefixe kannst Du in Knoten, die keine Blätter sind, ablegen. Ich nehme mal an, dass Du recht wenige (ca. 10) Ergebnisse einer Anfrage tatsächlich brauchst.
    Damit solltest Du ungefähr doppelt so viele Einträge bekommen.
    Die Frage ist nun, wie man den Baum darstellt. Ordner mit Dateien sollte eigentlich mit geeignetem Dateisystem funktionieren.
    Alternativ dazu könnte man eine Tabelle dafür anlegen, die für alle Wörter und deren Prefixe Einträge enthält. Da bräuchte man aber wirklich für jeden Trie-Knoten ein Eintrag, wärend man bei der ersten Variante leicht Knoten zusammenfassen kann, wenn ein Knoten nur einen Unterknoten hat.

    Es kann natürlich sein, dass sich deine Datenbankstruktur oder Anfragen auch noch so optimieren lassen und das ausreicht. Wenn die Berechnung der Ergebnisse aber wirklich recht aufwendig ist, könnte das eine Möglicheit sein, vorausberechnete Ergebnisse zu speichern.

    Grüße

    Daniel

    1. Hallo Daniel,

      danke dir, das muss ich mal durchdenken. Ganz so trivial klingt die Lösung jetzt erstmal nicht ;-)

      Wenn ich es recht verstehe, wäre die einfachste Lösung die Speicherung im Dateisystem:
         /s/u/c/h/e/result.xml
         /s/u/p/e/r/result.xml
         /s/u/p/p/e/result.xml
      Pro Verzeichnis eine Datei. Ist die Frage, ob das dann die von Sven geäußerte Skepsis zum langsamen Dateizugriff entschärft?

      Aber ließe sich das nicht wiederum mit meinem Vorschlag weiter unten (Antwort auf Sven) kombinieren: selten benutzte Suchanfragen werden regelmäßig gelöscht, so dass ich bspw. zu jedem denkbaren Zeitpunkt nur 10.000 XML-Dateien speichere - die aber 90% aller Anfragen abdecken?

      Danke euch,
      Eddie

      --
      Old men and far travelers may lie with authority.
      1. Hallo Eddie,

        Pro Verzeichnis eine Datei. Ist die Frage, ob das dann die von Sven geäußerte Skepsis zum langsamen Dateizugriff entschärft?

        Die Skepsis teile ich nur bedingt. Die Ordner enthalten ja in dem Fall nur 27 Einträge und der Baum ist so ca. 5 Ebenen tief. Da sollte man sehr schnell den richtigen Treffer finden. Dein Problem sind ja auch nicht die 6 Mio. Einträge in der Datenbanktabelle, sondern dass Du zudem noch eine kompliziertere Anfrage hast.
        Wenn Du ähnlich komplizierte Berechnungen durchführen musst, um die Cachedateien zu finden, bringt der Cache natürlich nichts mehr. Wenn man aber statt dem Ausführen eines komplexen Queries nur einen Dateizugriff in einem flachen Verzeichnisbaum durchführen muss, sollte das schon einiges bringen.
        Ich habe mit sowas noch nie experimentiert, aber ich nehme an, dass moderne Dateisysteme auch noch mit ein paar Mio. Dateien performant arbeiten.

        Zum löschen seltener Anfragen: Das bringt es nur, wenn Du wirklich einige wenige Anfragen hast, die extrem häufig sind. Aber ist das wirklich der Fall?
        Außerdem würde ich davon keinen Performancevorteil erwarten. Im Schnitt wird das sicher sogar langsamer, weil einge Anfragen eben nicht gecacht sind.
        Du kannst dadurch höchstens Speicherplatz spaaren. Wenn es aber keinen Zwang gibt, damit zu geizen, würde ich das eher nicht tun.

        Grüße

        Daniel

  3. Hey,

    Also muss ich das Ganze irgendwie optimieren. Anbieten wuerde sich dabei etwa eine konsistente Speicherung der Ergebnisse als Datei.

    Fallstrick ist der Plattenzugriff hier. Schneller geht's über memcached. http://www.danga.com/memcached/

    --
    水-金-地-火-木-土-天-海-冥