Pour savoir où on va, il faut savoir d'où l'on vient

Vous avez
une question ?
Un projet ?

Contactez nous !
 

Contactez-nous

Vous avez une question ? un projet ? 
Vous souhaitez plus d'informations sur un produit ? sur notre offre ? 
Contactez-nous, on vous répond sous 4H.

retour

Cache de données

Cache de données

Cache de données

Le cache positionné entre application et gestion des données est essentiel également, particulièrement dans les architectures les plus modernes.

Approche ensembliste ou clé/valeur

Le cache de données n’est pas juste une gestion de données en mémoire, il fonctionne selon un paradigme différent, infiniment plus simple et donc plus performant. A l’inverse bien sûr, il lui manque énormément de fonctionnalités, à commencer par les propriétés ACID.

Le concept du SGBD est de nature ensembliste, c’est à dire que les requêtes que l’on adresse au SGBD sont en référence à un ensemble d’entités, défini par la clause WHERE, ou bien par une jointure.

C’est la force et la puissance des SGBD, qui permet en quelques mots, d’exprimer une sélection ou une mise à jour qui correspondraient à un traitement complexe.

Mais cette approche ensembliste présente aussi des inconvénients.

D’une part, elle est d’un maniement délicat. Les requêtes complexes sont pratiquement imprévisibles en termes de performances, et doivent subir un tuning spécifique pour être utilisables en production.

Plus important, l’approche ensembliste est difficilement compatible avec un partitionnement. Or le partitionnement est l’une des meilleures voies vers l’extensibilité.

Dans la pratique, il est fréquent qu’un SGBD ne soit pas utilisé de manière ensembliste, mais selon un paradigme clé-valeur, moins puissant, mais beaucoup plus simple. Et la programmation objet a accentué cette tendance.

Fondamentalement, le paradigme clé-valeur tient en deux primitives :

  • Lire valeur correspondant à clé : value=get(key);
  • Ecrire valeur correspondant à clé : set(key,value);

Pour ce type d’utilisations, le SGBD n’est parfois pas le bon outil.

Le cache gestionnaire de données

Comme on l’a vu en introduction, la vision traditionnelle est que la mémoire est certes très rapide, mais (1) trop chère donc trop rare et (2) non persistante donc trop fragile. Le modèle classique du cache part de ces deux hypothèses, supposant que (1) on ne peut pas compter avoir toutes les données en mémoire et (2) même si on le pouvait ce serait trop volatile.

Donc le dispositif de référence pour stocker les données est le disque, que ce soit géré par un SGBD ou directement en fichiers. Et on utilise le peu de mémoire dont on dispose en mode cache MRU.

Aujourd'hui, la mémoire est toujours aussi peu persistante, mais elle n'est plus rare. Elle peut donc devenir le moyen de stockage primaire, le gestionnaire de données. Non pas des données les plus utilisées, mais de toutes les données.

Si on a un millions de membres inscrits et 50 KO de données sur chacun d'eux, ça ne fait finalement que 50 GO de RAM à prévoir, ce qui n'est pas une grosse dépense. Surtout si c'est pour servir 200 000 requêtes par seconde. Donc on peut dire: la totalité de mes données sont en mémoire, tout le temps. Même si un utilisateur n'est pas venu depuis une semaine ou un mois, ses données sont en mémoire, comme les autres.

Malgré tout, la mémoire reste fragile, et il faut donc une sauvegarde persistante. C'est là qu'intervient le disque. Autrement dit, le disque joue dans ces architectures le rôle que jouaient les bandes auparavant:

c'est l'ultime recours, mais en temps ordinaire on n'en a pas besoin. Avec quelques différences tout de même. La « sauvegarde » sur le disque s’effectue en temps-réel, à chaque modification. Le disque reste en fait le lieu « de référence » pour l’information, mais non le lieu « de gestion ordinaire ».

Dans ce modèle, la gestion MRU demeure, mais elle n’est plus d’une grande importance. En revanche, le principe de l’alimentation du cache en mode pull, reste essentiel, car le principe continue d’être que l’on peut perdre tout ou partie du cache sans dommage. Dans ce cas, le cache sera reconstitué au fur et à mesure de la demande.

C’est une réflexion que l’on a représenté sur le schéma suivant :

image136

A gauche, l’ancien modèle, disons des années 90 : l’application s’adresse au SGBD qui stocke ses données sur disque, avec une certaine utilisation du cache, que nous n’avons pas représentée.

Au milieu, le modèle aujourd’hui classique. L’application manipule des objets, ces objets sont construits par la couche ORM à partir des

données gérées toujours dans le SGBD, avec un dispositif de cache entre les deux.

Dans les deux cas, le stockage disque est secouru sur des supports magnétiques offline.

A droite, le nouveau modèle, à la fois de très hautes performances et très haute extensibilité. On a placé ici le SGBD au niveau « secours », en même temps que ses disques. Il peut y avoir encore un secours offline de second niveau, mais la tendance est plutôt à gérer la totalité de l’archivage sur disque.

Dans les couches supérieure, on retrouve la modélisation objet, mais le cache prend une place nouvelle, il est le gestionnaire principal des données. A tout instant, toutes les données sont en mémoire quelque part dans le cache, sauf du moins dans quelques états transitoires.

Memcached

Memcached est un outil de cache open source, qui a la particularité d’être un cache distribué. C’est un cache générique, c’est à dire qu’il peut stocker n’importe quels objets, de 1 octet à 1 méga-octets environ.

Memcached fonctionne comme un serveur, au sens applicatif du terme. C’est à dire que, à la manière d’une base de données, les applications adressent des requêtes (sur TCP/IP) à memcached, qui leur répond.

Memcached fonctionne selon le paradigme du dictionnaire, c’est à dire une correspondance clé-valeur, à la manière des hashtable.

Les requêtes que les applications adressent à memcached sont très simples : lire valeur correspondant à clé, écrire valeur correspondant à clé.

Puisque memcached fonctionne pratiquement comme une hashtable, une simple fonction d’association (clé, valeur), pourquoi ne pas utiliser celle-ci ?

C’est là qu’intervient le caractère distribué. On peut lancer autant d’instances de memcached, sur autant de serveurs que l’on veut, elles se répartiront le travail, et le stockage, de manière totalement automatique et transparente.

Chaque paire clé-valeur ne sera stockée que dans une seule des instances de memcached. Le fonctionnement est d’une simplicité superbe. Lors de chaque accès, la clé est hashée, c’est à dire qu’on lui applique un petit algorithme très rapide, qui aboutit à un nombre, et à ce nombre on applique un modulo N, où N est le nombre d’instances. Le résultat de ce calcul indique l’instance à laquelle s’adresser. Une fois que la requête parvient à la bonne instance, le reste n’est qu’unaccès hashtable ordinaire. On voit bien que ce fonctionnement est extensible à l’infini.

Pour autant, avant de multiplier les instances, il faut analyser le besoin : tant qu’une instance unique n’est pas saturée, on n’a pas besoin de plus.

Comme tous les dispositifs de cache partiels, le cache distribué de memcached est naturellement robuste, tout simplement parce que si une donnée n’est plus disponible, on va la chercher à la source. Le cache ne porte aucune donnée de référence.

Du moins, c’est la manière recommandée de l’utiliser, mais rien n’interdit d’utiliser memcached comme seul gestionnaire des données, du moment que l’on a analysé les conséquences d’une perte.
Memcached est utilisé en particulier par Facebook, qui gère 800 serveurs de cache, totalisant 28 tera-octets de mémoire. Avec quelques optimisations de

memcached, Facebook annonce des performances de 200 000 requêtes par seconde par serveur .

EhCache

Ehcache fonctionne également en mode serveur ; il n’est pas distribué, mais plutôt répliqué.

Avec Ehcache, chaque objet en mémoire est répliqué sur les différentes instances Ehcache, contrairement au principe de memcached. Cette réplication présente nécessairement des limites en termes de scalabilité : le nombre de messages à échanger entre les instances croît plus ou moins avec le carré du nombre d’instances.

Pour cette réplication, Ehcache peut utiliser différents modes : lors de la modification d’un objet, les autres instances peuvent seulement invalider l’objet en cache, ou bien en recevoir la copie. C’est dans ce second cas que l’on peut réellement parler de réplication.

De même, la réplication peut être synchrone ou bien asynchrone.

Ehcache peut s’utiliser en temps que librairie simple, pour une application unique en environnement J2EE donc, ou bien en temps que serveur de cache, partagé par plusieurs applications.

Une nouvelle instance de Ehcache peut initialiser son cache auprès du cluster d’instances actives.

Ehcache supporte la persistance, c’est à dire qu’il peut conserver ses données sur disque lors de l’arrêt, et les reprendre à la relance.

Il supporte l’API java JSR107, JCache, mais peut également être utilisé en tant que serveur, sur des APIs REST ou SOAP. Le mode serveur le rend accessible à des applications de tous environnements techniques.

Les valeurs de time to live et time to idle peuvent être définies pour chaque instance, mais des valeurs par objet peuvent aussi être spécifiées, qui prévalent.

Il est capable d’avoir une stratégie d’éviction des moins fréquemment utilisés, et non simplement des moins récemment utilisés. Il y a une petite nuance : un objet peut avoir été utilisé 100 fois à un instant T0, puis plus du tout pendant 10 secondes. Sa fréquence d’utilisation sur les 10 dernières secondes reste de 10 fois par seconde. Un second objet peut n’avoir été utilisé que 2 fois sur ces 10 secondes, mais la dernière fois était à la dixième seconde. S’il faut en purger un, alors la politique LRU purge le premier, tandis que la politique LFU purge le second.