- Blog Xebia France - http://blog.xebia.fr -
Les caches, ces armes à manipuler avec précaution
Posted By Erwan Alliaume On Mercredi 26 août 2009 @ 6:28 In Java / JEE,Performance | No Comments
Il n’y a rien de plus simple qu’un framework de cache … enfin il paraît. La majorité d’entre eux fonctionne sur un même principe élémentaire, celui que vous décideriez probablement d’adopter si l’on vous demandait d’en développer un : une bonne vieille HashMap ! Logique puisqu’il ne s’agit finalement que d’organiser manuellement un index d’objets vous permettant d’accéder le plus rapidement possible à vos données plutôt que d’aller les piocher dans des sources de données plus gourmandes. Et pourtant, leur utilisation est soumise à un certain nombre de cas d’utilisations offrant de belles occasions de se planter en beauté ! Cet article décrit quelques-uns de ces problèmes, plus précisément les problèmes liés au cache L2 hibernate et caches distribués.
C’est probablement l’une des phobies les plus répandues dans le monde informatique, partagées aussi bien par les développeurs, les architectes que les clients : l’incohérence des données. Phobie compréhensible puisque les données représentent la valeur ajoutée même de votre métier, sans elles, pas de transactions donc pas de rentrées d’argent. Et pourtant, nous constatons trop souvent que cette phobie est le point de départ de bien des problèmes. Combien d’usines à Shadocks ont été montées à tort ? Combien de fausses bonnes idées ont coulé des projets ? Ceci vient souvent d’une mauvaise interprétation des besoins du client : la perte ou l’incohérence des données est dans bien des cas acceptables. Alors, pourquoi s’obstiner à se rajouter systématiquement des aiguilles dans la poupée vaudou de votre projet.
En résumé, les problématiques liées à la cohérence des données d’un cache distribué sont les mêmes que celles d’une source de données partagée telle qu’une base de données : mises à jour concurrentes, échecs de mise à jour, fiabilité des contraintes… L’ajout d’un cache distribué n’est donc pas anodin, vous rajoutez avec lui une complexité complémentaire à votre système.
En cas de modification locale d’un élément d’un des caches d’un cluster, deux solutions s’offrent à vous pour répercuter celle-ci sur les différents nœuds de votre cluster :
Si vous êtes le seul à connaître l’existence d’un élément (i.e. l’objet a été créé localement et n’est directement disponible dans aucune source de données), vous n’avez d’autre choix que de propager vos modifications par copie. Dans tous les autres cas, c’est à vous de choisir la meilleure option en fonction du type de vos données.
Il est souvent recommandé de privilégier les mises à jour par copie. Celles-ci évitent le rechargement complet des caches de votre cluster. Suivant le type de données, vider entièrement un cache peut s’avérer désastreux : l’invalidation peut provoquer une demande massive d’objets simultanée à partir des différents nœuds d’un cluster. Les caches regroupent souvent des données homogènes dont les objets sont liés les uns aux autres. Il est facile de noyer la base sous une pluie de requêtes gourmandes en ressources et simultanée. Le but premier de votre cache distribué n’était pas de limiter les accès à la base de données ? Cet effet est encore amplifié par la fréquence de mise à jour de vos données. Dernier conseil, une mise à jour par copie perd de son intérêt lorsque que la durée de vie maximale des objets de vos caches est faible, surveillez votre time-to-live.
Pourtant, la mise à jour par copie n’est pas sans risque, vous exposez vos objets à de potentielles désynchronisations d’états entre les différents nœuds de votre cluster. En effet, la majorité des mécanismes de mise à jour par copie n’assurent pas la réussite de celle-ci. Par exemple, si une micro coupure réseau intervient lors de la demande d’une mise à jour d’un objet entre 2 serveurs, il est courant de constater qu’au final les deux objets ne soient pas identiques. (il en est d’ailleurs le même pour les mécanismes d’invalidation). Si les mécanismes de copies asynchrones sont rapides, ils ne garantissent pas la bonne exécution de leur service. Au contraire, d’autres mécanismes synchrones le permettent, mais à quel prix pour vos utilisateurs ? Êtes-vous prêt à stopper l’activité de votre serveur en attendant d’obtenir les accusés de réception de l’ensemble des nœuds de votre cluster.
| Mise à jour Asynchrone VS Synchrone | Mise à jour par copie VS invalidation |
|---|---|
| Réponse rapide contre cohérence des données | Multiplicité des notifications contre rechargement massif de la base |
Lorsque l’on sort le Cache L2 de la trousse à outils, on en attend souvent un résultat miraculeux. Pourtant, si cette solution est simple à mettre en place, elle n’est pas magique pour autant. Il est donc important d’en connaître les principales caractéristiques.
Les manipulations de données des caches s’effectuent le plus souvent par l’ajout ou la récupération d’un seul et unique objet. Les caches proposent tous une manipulation simple par le biais de ces simples méthodes : cache.put( Element element ) pour l’ajout d’un objet dans le cache et cache.get ( Object key ) pour la récupération. Et ce sont ces mêmes méthodes que le cache L2 Hibernate va utiliser. Ainsi, la mise à jour du cache, quelque soit son implémentation, va passer par une mise à jour objet par objet. Ceci s’avère peu performant lorsque le cache doit mettre à jour de volumineuses listes d’objets.
Vous l’avez compris, les effets peuvent vite devenir catastrophiques en cas de mauvaise utilisation des associations Hibernate. Il est parfois pertinent d’utiliser un système de cache externe sans utiliser le cache L2 Hibernate. Afin d’appuyer cela, je vous invite à lire le billet de blog suivant dans lequel l’auteur effectue une comparaison des performances en fonction des différentes utilisations du cache dont voici les résultats :
Pour 50 objets chargés contenant chacun 2 relations One-To-Many
Ce billet n’avait pas pour but de lister l’ensemble des problématiques liées aux caches distribués, j’en suis d’ailleurs incapable ; mais juste de mettre en lumière les principales problématiques liées au caching. Qu’il soit distribué ou non, la mise en place d’un cache ne doit pas s’effectuer à la légère. Si elle est censée améliorer les performances, elle en augmente grandement l’entropie de votre système. C’est pourquoi il est nécessaire d’avoir les moyens d’estimer le gain de performances lié à la mise en place de vos caches. La majorité des API proposent pour cela de la consultation des statistiques via JMX. Compter le nombre de hits (éléments tirés du cache) et de misses (éléments non trouvés dans la cache) vous permettra d’évaluer facilement le rendement de celui-ci.
Si vous ne deviez retenir qu’une chose, un cache c’est comme un pare-chocs de voiture pour une base de données :
Attention aux accidents ![]()
Article printed from Blog Xebia France: http://blog.xebia.fr
URL to article: http://blog.xebia.fr/2009/08/26/les-caches-ces-armes-a-manipuler-avec-precaution/
Click here to print.