Caching bei Datenbank-basierten Systemen

antimon

Mitglied
Hallo zusammen,

ich hätte mal eine grundsätzliche Frage, die mich schon seit langem verfolgt... und zwar, wie ich einen möglichst vernünftigen Datenzugriff bekomme. Dabei ist mir klar, dass es keine Schwarz/Weiss-Aussagen geben kann, da die optimale Lösung vom Anwendungsfall abhängt. Aber mir fehlt schlichtweg die Erfahrung, das beurteilen zu können.

Angenommen, eine Anwendung nutzt eine (MySQL-)Datenbank via Hibernate, die Kundenadressen enthält. Gehen wir einfach mal von 1000 Datensätzen aus, die in einer grafischen Oberfläche (RCP-Anwendung) bearbeitet werden können sollen.

Macht es Sinn, die 1000 Datensätze einmal aus der Datenbank auszulesen, im Speicher zu behalten oder damit zu arbeiten? Sprich in einer Liste von Adress-Objekten im Model?
Oder würde eine getList()-Methode besser direkt auf die Datenbank zugreifen?
In dem Zusammenhang: Was bietet sich eher an? Die Daten per SQL zu filtern oder erst nachdem alle geladen wurden?

Und wie läuft das beim Anlegen/Bearbeiten/Löschen sinnvollerweise ab? Wenn alle Datensätze geladen sind, diese verwerfen, die Änderungen an der DB durchführen und alle neu laden? Oder versuchen, die gecachten Daten und die Datenbank parallel zu bearbeiten (kann Inkonsistenzen hervorrufen) - oder alle Änderungen "live" an der Datenbank durchzuführen?

Wie gesagt, mir ist klar dass es nicht _die_ perfekte Lösung gibt, aber wie lautet da Eure Erfahrung, könnt Ihr mir da Tips geben? Betrachtet werden sollte natürlich die Performance, die Aufwändigkeit des Implementierens und die Praxistauglichkeit. Alles halt in einem möglichst sinnvollen Verhältnis. Z.B. in der Art: "Das Laden der Daten geht so schnell, dass ein Caching gar kein Sinn ergeben würde". Oder: "bei wenigen Datensätzen bringt Caching nichts, bei mehreren 10.000 schon".

Vielleicht habt Ihr auch einen dazu passenden Link? Ich habe leider nichts vernünftiges dazu finden können...
 

slowfly

Erfahrenes Mitglied
Salve

Macht es Sinn, die 1000 Datensätze einmal aus der Datenbank auszulesen, im Speicher zu behalten oder damit zu arbeiten?
Wenn man z.B. einen Report generiert, kann das Sinn machen, ja. Da ist ein Zugriff schneller als 1000 Zugriffe. Wenn du aber mit wenig Speicher über die Runden kommen musst, kommt man je nachdem nicht drumrum, das ganze z.B. in 25er Schritte zu laden.
Aber nur um eine Liste mit 25 Einträgen anzuzeigen, lädt man 25 Einträge und lädt die nächsten 25 nach.

Was bietet sich eher an? Die Daten per SQL zu filtern oder erst nachdem alle geladen wurden?
Probiere immer wenn möglich mit where-clauses zu arbeiten. 1. Ist die DB für solche Spässe ausgelegt, 2. werden weniger Daten übertragen und 3. brauchst du weniger Speicher. Bei 1000 Datensätzen merkt man das vielleicht nicht sonderlich, aber über zwei Milliarden Rows Daten selektieren, das kommt in keinen Heap rein ;-)

Und wie läuft das beim Anlegen/Bearbeiten/Löschen sinnvollerweise ab? Wenn alle Datensätze geladen sind, diese verwerfen, die Änderungen an der DB durchführen und alle neu laden?
Das ist jetzt schon eine kurlige Frage... Anlegen: Datensatz speichern. Bearbeiten: Datensatz laden, bearbeiten, speichern. Löschen: Datensatz löschen. Aber ich vermute mal, das ist nicht das, was du wissen wolltest?

Und bezügliche "Daten schnell laden", Datenmengen, etc:
Das kommt auf die Datenbank drauf an, wie sie konfiguriert ist, auf was für einem Server sie läuft und wo sie steht. Und natürlich müssen die Anforderungen und SLA's an die Applikation berücksichtigt werden.

Links:
http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/performance.html
http://www.amazon.de/bücher/dp/3868020403

Gruss,
slowy
 

antimon

Mitglied
Hallo slowy,

danke schon mal für deine Antwort!

Ich versuche mal, meine Frage umzuformulieren - vielleicht wird es dann etwas klarer... ;)

Angenommen, es gibt eine Datenbanktabelle mit Kundenadressen... z.B. 10.000, damit mal "mehr" Daten vorhanden seien. Diese sollen in einer Tabelle in der GUI dargestellt werden - natürlich möglichst performant.
Das heisst, es gibt normalerweise einen Filter, der z.B. nur Datensätze mit dem Anfangsbuchstaben "A" anzeigt, das müsste auf eine WHERE-Bedingung gemappt werden.

Bisher waren eigentlich alle meine Daten im Model z.B. in einer LinkedList gespeichert (also im Speicher) - beim Anzeigen in einer Tabelle sorgt ein TableFilter dafür, dass bestimmte Objekte ausgefiltert wurden, ein Sorter sorgt dafür dass die Daten in der richtigen Reihenfolge angezeigt werden. Das Sortieren und Filtern geschieht dabei ausserhalb des Models, der Datenbestand im Model ist immer gleich.

Bei einer Datenbank verlagert sich das Ganze ja etwas... dort ist das Model quasi nur ein Proxy zu den Daten, kann man das so sagen? Und die Filterung geschieht schon bei der Abfrage der Datenbank, also nicht erst durch die zugreifenden Objekte. Dadurch enthält das Model quasi gar keine Daten, sondern reicht sie nur durch? Wie bzw. wo findet dann aber die Filterung bzw. Sortierung statt?

Angenommen ich habe zwei Ansichten in der GUI, und zwar eine Liste aller Kunden, gefiltert nach dem Anfangsbuchstaben - sowie eine Ansicht der Top10-Kunden, die 10 Kunden herausfiltert und zwar sortiert nach dem Umsatz. Wie würde ich dann sinnvollerweise den Datenbankzugriff gestalten? Prinzipiell doch schon über ein und dasselbe Model, denn ändert sich etwas an den Datensätzen, sollten ja alle Anzeigen darüber informiert werden - irgendwo müssen sich ja die Listener registrieren. Aber wie/wo werden die Filter (also die WHERE-Bedingungen) definiert?

Vielleicht denke ich zu kompliziert oder habe noch nicht genug Doku gewälzt (und da hab ich schon ne Menge gewälzt ;) ), aber irgendwie komme ich da auf keinen grünen Zweig.

Danke übrigens auch für den Buchlink!
 

slowfly

Erfahrenes Mitglied
Guten Morgen

Angenommen, es gibt eine Datenbanktabelle mit Kundenadressen... z.B. 10.000, damit mal "mehr" Daten vorhanden seien.
Hehe, das kommt halt auf die Applikation und deren Blech drauf an. Wir haben Applikationen, die arbeiten mit 16GB Heap und Millionen von Rows. Wir haben auch Applikationen, da werden Daten über Milliarden von Rows selektiert. Und wenn ich zu unseren DBA's gehe und "Performance-Probleme" und "10'000 Rows" in einem Satz nennen würde, würden die mich auslachen (kurze Anekdote zu Beginn - egal :p)

Nunja, also "normalerweise" probiert man ja die Datenzugriffsschicht (DAO's) zu entkoppeln. Da bietet man ein Interface mit den Methoden "getById, search, delete, save" etc an. Wenn man jetzt von "in-memory" zu "Datenbank" wechselt, implementiert man einfach das Interface in einer neuen Klasse.

Und ein Proxy ist es nicht. Ein Proxy stellt man vor eine Klasse, die man aufruft. Da kann man zum Beispiel einen "PerformanceProxy" schreiben, welcher vor einem Methoden aufruf die Zeit merkt und nach dem Aufruf die Differenz misst und ausgibt... aber am besten mal selber implementieren, ich hab's erst dann verstanden.

Das "Model" als Geschäftsmodell oder business logic sollte eigentlich traditionell nichts mit dem Datenzugriff zu tun haben (Stichwort lose Kopplung (Komponenten möglichst unabhängig voneinander entwerfen) und hohe Kohäsion (die Dinge dort reintun wo sie hingehören)). In der traditionellen Mehrschichtenarchitektur hat man ja Präsentationslogik, Geschäftslogik, welche der Präsentationslogik die Daten zum Anzeigen zur Verfügung stellt und die Datenzugriffslogik, welcher die Daten lädt, transformiert und persistiert. Diese müssen möglichst austauschbar sein - ansonsten hat ja alles nüx gebracht.

Zu deiner Annahme:
Zu den Top 10: Also wenn du 10'000 Kunden hast und jeder Kunde jeden Tag eine Transaktion tätigt, ergibt das in einem Jahr,... ja, ziemlich eine Menge. Da würde ich sogar nen Job einplanen, der das all paar Minuten ausrechnet und in einer eigenen Tabelle speichert.

Wie würde ich dann sinnvollerweise den Datenbankzugriff gestalten?
Uhm die Frage sollte sich eigentlich anhand meiner Schilderungen zur Mehrschichtenarchitektur erübrigen, oder?

Aber wie/wo werden die Filter (also die WHERE-Bedingungen) definiert?
Nunja, das kann man auf viele Wege machen. Man kann natürlich bestimmte Filter direkt als Methode im DAO anbieten (searchByLastName), oder aber auch das ganze indexieren, ist auch ein interessantes Thema. Aber wenn man natürlich eine etwas komplexere Suche implementieren muss, also vor Allem wenn's dann viele Suchkriterien gibt, lowercase, uppercase, startet mit, endet mit, enthält, ganz gleich uuund so weitere, kann man auch eine kleine search-api schreiben.

Gruss,
slowy
 

antimon

Mitglied
Hallo,

also die Berechnung der Umsätze meinte ich jetzt nicht, es ging eher um die Darstellung der Daten.
Angenommen, jeder Kunde hat neben seinem Namen im gleichen Datensatz den Umsatz als Wert hinterlegt. Nun soll es zwei Views in Eclipse geben, einer mit der alphabetischen Kundenliste und einmal mit absteigend sortiertem Umsatz. Sprich, es gibt zwei unterschiedliche Ansichten des gleichen Datenbestandes. Einmal habe ich eine alphabetische Sortierung nach dem Kundennamen und einmal eine Sortierung nach dem Feld Umsatz. Wo sollte da die Sortierung/Filterung statfinden?
Die Anzeige der Kundendaten wäre die Präsentationslogik, aber die Datenzugriffslogik sollte ja die entsprechend angeforderten Daten richtig herausgeben, für jeden View getrennt.

Das ist mir eben noch nicht ganz klar, wie man sowas realisiert - wenn sich die Präsentationslogik um die Filterung kümmert, muss die Datenbank ja erst mal alle Datensätze liefern. Einem JFace TableViewer kann man ja beispielsweise einen Filter übergeben, aber der filtert ja erst nachdem er alle Datensätze erhalten hat. Das würde ja bedeuten, dass die Filterung nicht schon in der Datenbank geschehen kann.

Andersrum könnte man auf den Filter im Viewer verzichten und beim Datenzugriff ein Filter-Objekt übergeben, das den Filter dann in eine WHERE-Bedingung umwandelt und vorgefilterte Datensätze liefert. Allerdings muss dieser Filter dann so universell sein, dass er auch für in-memory-Daten funktioniert, sonst bringt das Interface wie du oben beschrieben hast, ja auch nichts.

Sprich, es läuft wohl auf die angesprochene Search-API hinaus, aber da fehlen mir Anhaltspunkte, wie man sowas angehen kann - bzw. gibt es da nichts fertiges? Prinzipiell ist die Aufgabenstellung doch gar nicht so untypisch oder?
 

slowfly

Erfahrenes Mitglied
Salve

Ja, eben, das kommt drauf an. Wenn dein Programm mit 10'000 Datensätzen gut arbeiten kann, kannst du ja initial die Daten aus der DB lesen und in-memory sortieren - geht beim Start etwas länger, aber die Arbeit damit geht super schnell. Wenn pro Jahr 1% Datensätze mehr hinzukommen, wird das die Applikation auch in 5 Jahren noch können, bis dahin wird es sogar neue Clients oder Server geben. Wenn du hingegen mit Paging arbeitest und "oft" auf die DB gehst, ist die Applikation zwar schneller gestartet, dafür muss man öfter auf die Datenbank, was je nachdem spürbar langsamer sein kann. Dann kann man solche Dinge machen wie zuerst nur die Daten zu laden, die effektiv nur angezeigt werden und im "Detail" dann den kompletten Satz nachladen...
Wenn du aber sagen wir mal an einem Stichtag Werbung intialisierst und keinen "Wann-Geht-meine-Applikation-In-Die-Brüche"-Test (fällt der korrekte Name nicht grad ein) durchführst und dann pro Tag 2% über die nächsten 100 Tage bekommst hast du dann so ungefähr 50-60k Datensätze. Wenn die Hardware und Applikation dafür nicht ausgelegt ist, hast du in diesem Moment ein ziemlich heftiges Problem: Es läuft nämlich gar nichts mehr und deine Kunden können nicht arbeiten, das gibt dann Nachtschicht. Dann hätte man lieber ein Paging gemacht. Aber evtl. bleibt der Datenbestand ja prinzipiell mal bei 10k, dann kann man das ganze aus der DB laden und nur mit in-memory-Sortierung arbeiten, dann ist die DAO-Logik sehr reduziert.

Ich kann dir hier keine optimale Lösung anbieten, ohne dabei die Systemarchitektur, Hardwarespezifikationen, Datenbankmodell, Ausblicke, etc. zu kennen. Darum gibt es auch keine Antwort auf "wie man das am besten realisiert".

Gruss,
slowy
 

antimon

Mitglied
Also dass es keine perfekte Antwort gibt, ohne die Details zu kennen, ist mir klar. Mir geht es auch eher um das grundsätzliche, wo wie was gemacht wird.

Mal ein konkretes Beispiel: Angenommen, es werden Daten (sagen wir einfach mal 10 Datensätze) aus einer Datei gelesen. Beim Start des Programms werden die in eine Liste eingelesen und wenn man in der GUI auf Speichern klickt oder das Programm beendet werden die Daten aus dem Speicher zurückgeschrieben. Solange niemand anderes mit den Daten aus der Datei arbeitet, ist das ja auch kein Problem.

Sprich, es gibt ein Model, habe ich neue Datensätze, übergebe ich dem Model diese per add(...) oder lösche welche mit delete(...) und wenn ein Objekt selbst modifiziert wird, ist das auch kein Problem, beim Speichern wird ja der Inhalt der kompletten Liste geschrieben.

Bei einer Datenbank läuft das ja vermutlich anders - erstens müsste ich alle Daten aus der DB löschen bevor ich speichere, das Überschreiben wie bei einer Datei geht ja nicht. Das Hinzufügen von neuen Datensätzen könnte ich auch per add(...) realisieren, dabei wird dann halt im Hintergrund INSERT-Befehl gefahren und gut ist.

Aber wenn ich nun Objekte manipuliere, die Datensätze in der Datenbank darstellen - wie speichere ich die nun? Mein Model müsste ja nun Änderungen am Datensatz mitbekommen und daraufhin ein UPDATE fahren - aber wegen jeder Änderung an einer Eigenschaft wäre das wohl viel zu viel.

Es geht ja eigentlich darum, durch das Model von der darunterliegenden Schicht (Datei, Datenbank, ...) zu abstrahieren... aber auch dadurch dass die Datenbank in der Zwischenzeit von jemand anderem geändert wurde, muss ich diese ja regelmäßig neu laden, um die Änderungen mitzubekommen. Das habe ich ja bei einer Datei (oder einer lokalen Datenbank), auf die nur ich zugreife, nicht. Wie bringe ich also die Abstraktion durch das Model hin?

Vermutlich führen auch hier viele Wege nach Rom, aber zumindest ein Wegweiser würde mir schon helfen, da ich da absolut auf dem Schlauch stehe, aber nicht in die falsche Richtung programmieren möchte...
 

slowfly

Erfahrenes Mitglied
Moin

Aber wenn ich nun Objekte manipuliere, die Datensätze in der Datenbank darstellen - wie speichere ich die nun? Mein Model müsste ja nun Änderungen am Datensatz mitbekommen und daraufhin ein UPDATE fahren - aber wegen jeder Änderung an einer Eigenschaft wäre das wohl viel zu viel.
Da gibt es halt diese zwei Ansätze: Jede Eigenschaft einzeln ändern oder komplette Datensätze ändern, wobei das zweitere vermutlich eher verbreitet ist... das erstere habe ich jetzt glaub ich noch nie angewendet.

aber auch dadurch dass die Datenbank in der Zwischenzeit von jemand anderem geändert wurde, muss ich diese ja regelmäßig neu laden, um die Änderungen mitzubekommen.
Da gibt's auch zwei Ansätze. Optimistic und Pessimistic Locking, hier ist's gut beschrieben:
http://www.dbasupport.com/forums/sh...ing-vs.-Pessimistic-Locking&p=29149#post29149

Gruss,
slowy