Schreiben Sie niemals Ihre Caching-Lösung

Eine weitere Möglichkeit, die Arbeit mit der Datenbank zu beschleunigen, besteht darin, Objekte zwischenzuspeichern, die wir bereits zuvor angefordert haben.

Wichtig! Schreiben Sie niemals Ihre eigene Caching-Lösung. Diese Aufgabe birgt so viele Fallstricke, von denen Sie nie zu träumen gewagt hätten.

Problem 1 – Cache-Flush . Manchmal treten Ereignisse auf, wenn ein Objekt aus dem Cache entfernt oder im Cache aktualisiert werden muss. Die einzige Möglichkeit, dies kompetent zu tun, besteht darin, alle Anfragen über die Cache-Engine an die Datenbank weiterzuleiten. Andernfalls müssen Sie dem Cache jedes Mal explizit mitteilen, welche Objekte darin gelöscht oder aktualisiert werden sollen.

Problem 2 – Speichermangel . Caching scheint eine gute Idee zu sein, bis Sie feststellen, dass Objekte im Speicher viel Platz beanspruchen. Sie benötigen zusätzliche Dutzende Gigabyte Arbeitsspeicher, damit der Serveranwendungs-Cache effektiv funktioniert.

Und da es immer zu einem Speichermangel kommt, ist eine effektive Strategie zum Löschen von Objekten aus dem Cache erforderlich. Dies ähnelt in gewisser Weise dem Garbage Collector in Java. Und wie Sie sich erinnern, haben die besten Köpfe seit Jahrzehnten verschiedene Methoden erfunden, um Objekte nach Generationen usw. zu kennzeichnen.

Problem 3 – verschiedene Strategien . Wie die Praxis zeigt, sind für verschiedene Objekte unterschiedliche Strategien zum Speichern und Aktualisieren im Cache wirksam. Ein effizientes Caching-System kann nicht nur eine Strategie für alle Objekte anwenden.

Problem 4 – Effiziente Speicherung von . Sie können Objekte nicht einfach im Cache speichern. Objekte enthalten zu oft Verweise auf andere Objekte usw. In diesem Fall benötigen Sie keinen Garbage Collector: Es gibt einfach nichts zu entfernen.

Anstatt die Objekte selbst zu speichern, ist es daher manchmal viel effizienter, die Werte ihrer primitiven Felder zu speichern. Und Systeme zur schnellen Konstruktion darauf basierender Objekte.

Als Ergebnis erhalten Sie ein komplettes virtuelles DBMS im Speicher, das schnell arbeiten und wenig Speicher verbrauchen soll.

Datenbank-Caching

Neben dem Caching direkt in einem Java-Programm wird das Caching oft auch direkt in der Datenbank organisiert.

Es gibt vier große Ansätze:

Der erste Ansatz besteht darin, die Datenbank zu denormalisieren . Der SQL-Server speichert Daten anders im Speicher als in Tabellen.

Wenn Daten in Tabellen auf der Festplatte gespeichert werden, versuchen Entwickler sehr oft, Datenduplizierungen so weit wie möglich zu vermeiden – dieser Vorgang wird als Datenbanknormalisierung bezeichnet. Um die Arbeit mit Daten im Speicher zu beschleunigen, wird der umgekehrte Vorgang durchgeführt – die Denormalisierung der Datenbank. Eine Reihe zusammengehöriger Tabellen können bereits in kombinierter Form gespeichert werden – in Form riesiger Tabellen usw.

Der zweite Ansatz ist das Abfrage-Caching . Und Abfrageergebnisse.

Das DBMS stellt fest, dass sehr oft die gleichen oder ähnliche Anfragen eingehen. Dann beginnt es einfach mit dem Zwischenspeichern dieser Anfragen und ihrer Antworten. Gleichzeitig müssen Sie jedoch sicherstellen, dass in der Datenbank geänderte Zeilen rechtzeitig aus dem Cache entfernt werden.

Dieser Ansatz kann sehr effektiv sein, wenn ein Mensch Abfragen analysieren und dem DBMS dabei helfen kann, herauszufinden, wie sie am besten zwischengespeichert werden.

Der dritte Ansatz ist eine In-Memory-Datenbank .

Ein weiterer häufig verwendeter Ansatz. Zwischen dem Server und dem DBMS wird eine weitere Datenbank platziert, die alle ihre Daten nur im Speicher speichert. Es wird auch In-Memory-DB genannt. Wenn viele verschiedene Server auf dieselbe Datenbank zugreifen, können Sie mithilfe von In-Memory-DB das Caching basierend auf dem Typ eines bestimmten Servers organisieren.

Beispiel:

Ansatz 4 – Datenbankcluster . Mehrere schreibgeschützte Basen.

Eine andere Lösung ist die Verwendung eines Clusters: Mehrere DBMS desselben Typs enthalten identische Daten. Gleichzeitig können Sie Daten aus allen Datenbanken lesen und nur in eine schreiben. Die dann mit den übrigen Datenbanken synchronisiert wird.

Dies ist eine sehr gute Lösung, da sie einfach zu konfigurieren ist und in der Praxis funktioniert. Normalerweise gehen für eine Anfrage an die Datenbank zum Ändern von Daten 10–100 Anfragen zum Lesen von Daten ein.

Arten des Cachings im Ruhezustand

Hibernate unterstützt drei Caching-Ebenen:

  • Caching auf Sitzungsebene (Session)
  • Caching auf SessionFactory-Ebene
  • Caching-Anfragen (und ihre Ergebnisse)

Sie können versuchen, dieses System in Form einer solchen Figur darzustellen:

Die einfachste Art des Cachings (auch First-Level-Cache genannt ) wird auf der Hibernate-Sitzungsebene implementiert. Der Ruhezustand verwendet standardmäßig immer diesen Cache und kann nicht deaktiviert werden .

Betrachten wir zunächst das folgende Beispiel:

Employee director1 = session.get(Employee.class, 4);
Employee director2 = session.get(Employee.class, 4);

assertTrue(director1 == director2);

Es mag den Anschein haben, dass hier zwei Abfragen an die Datenbank ausgeführt werden, aber das ist nicht der Fall. Nach der ersten Anfrage an die Datenbank wird das Employee-Objekt zwischengespeichert. Und wenn Sie das Objekt in derselben Sitzung erneut abfragen, gibt Hibernate dasselbe Java-Objekt zurück.

Dasselbe Objekt bedeutet, dass auch Objektreferenzen identisch sind. Es ist wirklich das gleiche Objekt.

Die Methoden save() , update() , saveOrUpdate() , load() , get() , list() , iterate() und scroll() verwenden immer den Cache der ersten Ebene. Eigentlich gibt es nichts mehr hinzuzufügen.

Caching der zweiten Ebene

Wenn der Cache der ersten Ebene an das Sitzungsobjekt gebunden ist, ist der Cache der zweiten Ebene an das Sitzungsobjekt gebunden.SessionFactory. Das bedeutet, dass die Sichtbarkeit von Objekten in diesem Cache viel breiter ist als im Cache der ersten Ebene.

Beispiel:

Session session = factory.openSession();
Employee director1 = session.get(Employee.class, 4);
session.close();

Session session = factory.openSession();
Employee director2 = session.get(Employee.class, 4);
session.close();

assertTrue(director1 != director2);
assertTrue(director1.equals(director2));

In diesem Beispiel werden zwei Abfragen an die Datenbank durchgeführt. Hibernate gibt identische Objekte zurück, aber es wird nicht dasselbe Objekt sein – sie werden unterschiedliche Referenzen haben.

Das Caching der zweiten Ebene ist standardmäßig deaktiviert . Daher haben wir zwei Abfragen an die Datenbank statt einer.

Um es zu aktivieren, müssen Sie die folgenden Zeilen in die Datei hibernate.cfg.xml schreiben:

<property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletEhCacheProvider"/>
<property name="hibernate.cache.use_second_level_cache" value="true"/>

Nachdem Sie das Caching der zweiten Ebene aktiviert haben, ändert sich das Verhalten im Ruhezustand ein wenig:

Session session = factory.openSession();
Employee director1 = session.get(Employee.class, 4);
session.close();

Session session = factory.openSession();
Employee director2 = session.get(Employee.class, 4);
session.close();

assertTrue(director1 == director2);

Erst nach all diesen Manipulationen wird der Second-Level-Cache aktiviert und im obigen Beispiel wird nur eine Abfrage an die Datenbank ausgeführt.