N'écrivez jamais votre solution de mise en cache

Une autre façon d'accélérer le travail avec la base de données consiste à mettre en cache les objets que nous avons déjà demandés précédemment.

Important! N'écrivez jamais votre propre solution de mise en cache. Cette tâche comporte tant d'embûches dont vous n'auriez jamais rêvé.

Problème 1 - vidage du cache . Parfois, des événements se produisent lorsqu'un objet doit être supprimé du cache ou mis à jour dans le cache. La seule façon de le faire de manière compétente est de transmettre toutes les requêtes à la base de données via le moteur de cache. Sinon, vous devrez à chaque fois indiquer explicitement au cache quels objets doivent être supprimés ou mis à jour.

Problème 2 - manque de mémoire . La mise en cache semble être une excellente idée jusqu'à ce que vous constatiez que les objets en mémoire occupent beaucoup d'espace. Vous avez besoin de dizaines de gigaoctets de mémoire supplémentaires pour que le cache de l'application serveur fonctionne efficacement.

Et comme il y a toujours un manque de mémoire, une stratégie efficace pour supprimer les objets du cache est nécessaire. Ceci est quelque peu similaire au ramasse-miettes en Java. Et comme vous vous en souvenez, depuis des décennies, les meilleurs esprits inventent diverses façons de marquer les objets par générations, etc.

Problème 3 - différentes stratégies . Comme le montre la pratique, différentes stratégies de stockage et de mise à jour dans le cache sont efficaces pour différents objets. Un système de mise en cache efficace ne peut pas appliquer une seule stratégie pour tous les objets.

Problème 4 - Stockage efficace de . Vous ne pouvez pas simplement stocker des objets dans le cache. Les objets contiennent trop souvent des références à d'autres objets, etc... À ce rythme, vous n'aurez pas besoin d'un ramasse-miettes : il n'aura simplement rien à supprimer.

Ainsi, au lieu de stocker les objets eux-mêmes, il est parfois beaucoup plus efficace de stocker les valeurs de leurs champs primitifs. Et des systèmes pour construire rapidement des objets basés sur eux.

En conséquence, vous obtiendrez tout un SGBD virtuel en mémoire, qui devrait fonctionner rapidement et consommer peu de mémoire.

Mise en cache de la base de données

En plus de la mise en cache directement dans un programme Java, la mise en cache est souvent organisée directement dans la base de données.

Il existe quatre grandes approches :

La première approche consiste à dénormaliser la base de données . Le serveur SQL stocke les données en mémoire différemment de la manière dont elles sont stockées dans les tables.

Lorsque les données sont stockées sur disque dans des tables, les développeurs essaient très souvent d'éviter autant que possible la duplication des données - ce processus s'appelle la normalisation de la base de données. Ainsi, pour accélérer le travail avec les données en mémoire, le processus inverse est effectué - la dénormalisation de la base de données. Un tas de tables liées peuvent déjà être stockées sous une forme combinée - sous la forme d'énormes tables, etc.

La deuxième approche est la mise en cache des requêtes . Et les résultats de la requête.

Le SGBD constate que très souvent des requêtes identiques ou similaires lui parviennent. Ensuite, il commence simplement à mettre en cache ces demandes et leurs réponses. Mais en même temps, vous devez vous assurer que les lignes qui ont changé dans la base de données sont supprimées du cache en temps opportun.

Cette approche peut être très efficace avec un être humain qui peut analyser les requêtes et aider le SGBD à trouver la meilleure façon de les mettre en cache.

La troisième approche est une base de données en mémoire .

Une autre approche couramment utilisée. Une autre base de données est placée entre le serveur et le SGBD, qui stocke toutes ses données uniquement en mémoire. Il est également appelé In-Memory-DB. Si plusieurs serveurs différents accèdent à la même base de données, l'utilisation de In-Memory-DB vous permet d'organiser la mise en cache en fonction du type d'un serveur particulier.

Exemple:

Approche 4 - grappe de bases de données . Plusieurs bases en lecture seule.

Une autre solution consiste à utiliser un cluster : plusieurs SGBD de même type contiennent des données identiques. En même temps, vous pouvez lire les données de toutes les bases de données et écrire dans une seule. Qui est ensuite synchronisé avec le reste des bases de données.

C'est une très bonne solution car elle est facile à configurer et fonctionne en pratique. Habituellement, pour une requête adressée à la base de données pour modifier des données, 10 à 100 requêtes de lecture de données lui parviennent.

Types de mise en cache dans Hibernate

Hibernate prend en charge trois niveaux de mise en cache :

  • Mise en cache au niveau de la session (Session)
  • Mise en cache au niveau SessionFactory
  • Mise en cache des requêtes (et de leurs résultats)

Vous pouvez essayer de représenter ce système sous la forme d'une telle figure :

Le type de mise en cache le plus simple (également appelé cache de premier niveau ) est implémenté au niveau de la session Hibernate. Hibernate utilise toujours ce cache par défaut et ne peut pas être désactivé .

Considérons immédiatement l'exemple suivant :

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

assertTrue(director1 == director2);

Il peut sembler que deux requêtes à la base de données seront exécutées ici, mais ce n'est pas le cas. Après la première demande à la base de données, l'objet Employee sera mis en cache. Et si vous interrogez à nouveau l'objet dans la même session, Hibernate renverra le même objet Java.

Le même objet signifie que même les références d'objet seront identiques. C'est vraiment le même objet.

Les méthodes save() , update() , saveOrUpdate() , load() , get() , list() , iterate() et scroll() utiliseront toujours le cache de premier niveau. En fait, il n'y a rien de plus à ajouter.

Mise en cache de deuxième niveau

Si le cache de premier niveau est lié à l'objet de session, alors le cache de second niveau est lié à l'objet de session.SessionFactory. Ce qui signifie que la visibilité des objets dans ce cache est beaucoup plus large que dans le cache de premier niveau.

Exemple:

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));

Dans cet exemple, deux requêtes seront adressées à la base de données. Hibernate renverra des objets identiques, mais ce ne sera pas le même objet - ils auront des références différentes.

La mise en cache de deuxième niveau est désactivée par défaut . Par conséquent, nous avons deux requêtes à la base de données au lieu d'une.

Pour l'activer, vous devez écrire les lignes suivantes dans le fichier hibernate.cfg.xml :

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

Après avoir activé la mise en cache de second niveau, le comportement d'Hibernate changera un peu :

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);

Ce n'est qu'après toutes ces manipulations que le cache de second niveau sera activé, et dans l'exemple ci-dessus, une seule requête vers la base de données sera exécutée.