Développeur Drupal – Blog d'un passionné, un peu touche à tout, dans le monde du web.
Qui n’a jamais eu besoin d’améliorer les performances de son site pour booster Drupal?
Effectivement, lorsque l’on travaille sur des gros sites il faut se poser des questions sur les performances de Drupal, parce qu’on le sait tous, Drupal est gourmand. L’affichage d’une simple page peut parfois engendrer l’exécution de 50 voir 150 requêtes.
Imaginez vous cette même page appelée par plusieurs internautes en même temps. On obtient alors des centaines de requêtes et informations recalculées inutilement qui vont solliciter les serveurs et vont ainsi consommer du CPU et de la RAM alors qu’elles auraient pu tout aussi bien être sauvegardées.
Pour sauvegarder ces informations il faut donc mettre en place un système de caching qui va mémoriser pour un temps donné des informations afin de ne pas les recalculer.
Le caching de page va avoir plusieurs impacts sur notre site :
La solution de caching n’est pas l’arme ultime pour améliorer les performances de votre site mais elle permet d’alléger la charge de travail du serveur et de le rendre plus disponible.
La solution la plus simple à mettre en place est d’activer le cache de page depuis l’administration de Drupal [admin/settings/performance]. C’est un cache simple mais efficace qui mémorise le résultat de la page. Malheureusement cette solution n’est disponible que pour la naviguation des utilisateurs anonymes, mais n’ayez crainte il existe belle et bien une solution pour les visiteurs authentifiés
Cette solution consiste à effectuer soi-même la mise en cache et l’affichage en utilisant l’Api de Drupal, mais cela implique néanmoins de savoir ce que vous voulez sauvegarder et afficher.
Celle-ci est composée de trois fonctions :
cache_set($cid, $data, $table = ‘cache’, $expire = CACHE_PERMANENT, $headers = NULL)
- $cid : C’est la clé permettant l’identification de ce que vous voulez sauvegarder. Celle-ci doit être unique, utilisez quelque chose qui ne pourra pas être créé par un autre module. Le plus simple est de préfixer votre clé avec le nom de votre module.
- $data : Ce sont les données à sauvegarder, ne vous inquiétez pas du typage et de la structure de vos données celles-ci sont sérialisées lors de l’enregistrement.
- $table : Par défaut vous enregistrerez dans la table nommé ‘cache’ mais vous pouvez tout aussi bien spécifier une autre table.
- $expire : Si vous souhaitez rafraîchir les informations stockées en cache vous allez devoir définir une période de validité pour celles-ci.
- $headers : Cette variable est utile si vous souhaitez passer des informations d’en-tête HTTP aux pages mises en cache.
cache_get($cid, $table = ‘cache’)
cache_clear_all($cid = NULL, $table = NULL, $wildcard = FALSE)
- $wildcard : Par défaut à False, cette variable peut, si elle est passée à True, sélectionner toutes les occurrences retrouvées commençant par le $cid renseigné.
function my_module_function() {
// Récupération du cache s'il existe
$cache = cache_get('my_module_data');
if (is_object($cache) && !empty($cache->data)) {
$my_data = $cache->data;
}
else {
// fonction de génération de votre contenu
// Sauvegarde des informations calculées.
cache_set('my_module_data', $my_data);
}
return $my_data;
}
Analysons le code ci-dessus simplifié pour l’exemple. Dans un premier temps la fonction cache_get() à été utilisée pour savoir si oui ou non, nous avions des données enregistrées a retourner directement à l’utilisateur. Dans le cas contraire les informations ont été générées puis mise dans la table de cache via la fonction cache_set().
Dans cette table vous pouvez stocker tout ce que vous voulez, un rendu html ou encore le calcul d’une lourde opération mais dites vous bien que les informations dans cette table ne sont qu’éphémères et peuvent à tout moment disparaître.
Il peut vous arriver de vouloir vider vos données sauvegardées tout simplement pour que celles-ci soient régénérées ou pour faire un export complet de votre base (ne prenez pas les données contenues dans les tables de cache, elle ne font que grossir la taille de votre export sans réel besoin).
Le nettoyage des caches se fait avec la fonction cache_clear_all() qui permet de supprimer ce qui nous concerne sans toucher au reste.
Par défaut celle-ci n’a besoin que d’une chaîne de texte ($cid) afin de trouver et effacer l’occurence dans la table de cache, mais si $wildcard est passé à True alors toutes les valeurs commençant par votre $cid seront effacées.
cache_clear_all('my_module_data', 'cache', TRUE);
Cet exemple supprime toutes les entrées de cache commencant par ‘my_module’.
Par défaut les informations que vous avez sauvegardées en cache sont gardées indéfiniment ou du moins jusqu’à ce que vous les ayez effacées avec la fonction cache_clear_all. La problématique sur la fraîcheur de données se pose alors car cette méthode n’est pas viable si vous souhaitez cacher des informations qui doivent être recalculées souvent.
Pour palier au cache permanent nous allons définir lors de l’enregistrement une date d’expiration aux données que l’on pourra comparer ensuite à la date en cours.
cache_set('my_module_data', $my_data, 'cache', time() + 300);
La date d’expiration doit être au format unix timestamp et la façon la plus simple pour définir cette valeur est d’ajouter une période en seconde au timestamp en cours. Cet exemple défini une validité de 5 minutes (60sec x 5).
Lorsque vous récupérez votre cache il ne vous reste plus qu’à vérifier qu’il est encore valide, sinon il faudra le régénérer et le mettre en cache.
function my_module_function() {
// Récupération du cache s'il existe.
$cache = cache_get('my_module_data');
if (is_object($cache) && !empty($cache->data)) {
// On vérifie que les données sont encore valide ou non.
if ($cache->expire > time()) {
return $cache->data;
}
}
// fonction de génération de votre contenu.
// Sauvegarde des informations calculées.
cache_set('my_module_data', $my_data);
return $my_data;
}
Nous venons de voir comment sauvegarder des informations dans la table de cache par défaut de Drupal, mais il est aussi possible de créer votre propre table de cache surtout si vous pensez avoir beaucoup d’informations à stocker. Ceci évitera d’avoir une grosse et unique table de cache.
Voici un petit exemple pour mieux comprendre l’interêt d’une table de cache dédiée. Imaginons que j’ai 200.000 urls raccourcies à mettre en cache. Par défaut cela aurait crée 200.000 nouveaux enregistrements dans la table de cache standard, ce qui aurait eu pour conséquence d’augmenter le temps d’exécution des requêtes sur la base de données parce que Mysql aurait été obligé de parcourir tous les enregistrements à chaque fois. En créant notre table dédiée on garde les performances de la table cache et on ne parcourt nos 200.000 valeurs qu’en cas de besoin.
Le plus simple à faire pour créer un autre table de cache est de faire une copie de la table standard ‘cache’ comme cela on est sûr d’avoir le même schéma de base de données. Pour cela la récupération du schema original et non modifié de la table ‘cache’ sera fait avec la fonction drupal_get_schema_unprocessed($module, $table = NULL).
/**
* Implementation of hook_schema().
*/
function my_module_schema(){
$schema = array();
$schema['cache_my_module'] = drupal_get_schema_unprocessed('system', 'cache');
return $schema;
}
Par convention nommez votre table ‘cache_’ puis le nom de votre module ce qui dans l’exemple ci dessus donne ‘cache_my_module’.
Le schema de base de données est prêt, il faut maintenant écrire les fonctions permettant la création et la suppression de celui-ci (hook_install et hook_uninstall) dans le fichier .install du module.
A ce stade la mise en place de la table de cache dédiée est terminée, il ne reste plus qu’à activer le module et à vérifier que celle-ci à bien été ajoutée à la base de donnée.
Pensez aussi à modifier les fonctions drupal_cache_get(), drupal_cache_set() et cache_clear_all() pour qu’elles pointent vers la bonne table.
cache_set('my_module_data', $my_data, 'cache_my_module');
cache_get('my_module_data', 'cache_my_module');
Un dernière chose sur le stockage des informations en cache, il est tout à fait possible de les sauvegarder vers un serveur spécialisé type memcached, APC, ou autre.
Vous voilà maintenant prêt à améliorer votre code. Si vous voulez en lire plus voici la présentation officielle de l’api cache de Drupal ou sinon le guide Lullabot sur l’utilisation de l’api.
Bienvenue sur mon blog. Sur cet espace j’essaie de partager au mieux ma passion pour le web et actuellement mon engouement pour Drupal. Vous trouverez ici mes découvertes, mes problématiques et les solutions rencontrées.
11 Responses to Jouez à cache-cache avec Drupal
Pierre
juin 16th, 2010 at 15 h 29 min
Super article , je vais tester
une petite remarque : il y a une fermeture de parenthese en trop dans cache_get(‘my_module_data’))
cf :
if ($cache = cache_get(‘my_module_data’)) && !empty($cache->data)) {
Drupal développeur
juin 18th, 2010 at 7 h 50 min
Je plussoie, c’est un très bel article, clair et précis.
En ce qui concerne la parenthèse en trop, je dirais plutôt qu’il en manque une avant le « !empty ». Mais chacun vois ça comme il le veut …
Julien
juin 18th, 2010 at 8 h 40 min
Après vérification il manquait bien un parenthèse, mais en début de ligne sans quoi la variable $cache ne prend pas de valeur et reste null
if (($cache = cache_get(‘my_module_data’)) && !empty($cache->data)) {
…
}
@Pierre et @Drupaldéveloppeur Merci pour le retour.
pounard
août 4th, 2010 at 9 h 05 min
Petite note à propos de cette fonction:
function my_module_function() { // Récupération du cache s'il existe $cache = cache_get('my_module_data'); if (is_object($cache) && !empty($cache->data)) { $my_data = $cache->data; } else { // fonction de génération de votre contenu // Sauvegarde des informations calculées. cache_set('my_module_data', $my_data); } return $my_data; }cache_get() ne cache pas statiquement le résultat de la récupération du cache, hors, si c’est sur un Drupal classique, cette fonction effectue systématiquement (au moins) une requête SQL, si c’est branché sur un memcache, une demande de cache sur un serveur distant.
Pour améliorer la fonction, il faut écrire ceci
function my_module_function() { static $my_data; if (!isset($my_data)) { // Récupération du cache s'il existe $cache = cache_get('my_module_data'); if (is_object($cache) && !empty($cache->data)) { $my_data = $cache->data; } else { // fonction de génération de votre contenu // Sauvegarde des informations calculées. cache_set('my_module_data', $my_data); } } return $my_data; }Dominique
août 14th, 2010 at 17 h 05 min
Est-ce que je peux remarquer que l’APC et le memcached ne sont pas des systèmes qui complètent la même tâche.
L’APC est un système d’opcode cache. PHP est une langue interpreté et ça veut dire chaque fois PHP doit génerer une page, le serveur doit includer plusieurs fichers et les compiler dans le code machine. Le système opcode cache diminue l’utilisation de memoire et diminue le temps pour processer. Tu peux voir l’effet d’un système opcode cache utilisant le module devel qui écrit les valeur d’utilisation mémoire et temps processer vers l’écran.
Le système memcached est un service au serveur qui met tout le data caché en mémoire RAM du serveur. Quand on Installe memcache, on remplace le système cache de coeur qui utilise les données de base. Drupal a un système de cache qui peut être remplacé par des autre système de cache comme memcache. Tous les fonctions comme cache_get, cache_set, en effet tout l’API de cache que vous expliquez très bien dans votre article continue à marcher. Pour un example comment remplacer le système cache default regarde au project http://drupal.org/project/memcache
Julien
août 17th, 2010 at 20 h 40 min
Pounard : Effectivement c’est une bonne solution de mettre une variable statique pour sauver ce qui à été récupéré du cache.
Je n’avais pas mis cette option car mon article étant déjà assez long je ne voulais pas mélanger le cache de drupal avec l’optimisation du code.
D’ailleurs une série de 3 posts sur le cache ne serait pas de trop (cache Drupal, cache d’opcode, serveurs de caches) un jour peut être.
Néanmoins maintenant que l’on inclut une variable statique il est nécessaire de mettre en place une possibilité de reset de cette variable. Pour optimiser cette fonction il faut donc écrire :
function my_module_function($reset = FALSE) { static $my_data; if (!isset($my_data) || $reset == TRUE) { if (!$reset && ($cache = cache_get('my_module_data')) && !empty($cache->data)) { $my_data = $cache->data; } else { // fonction de génération de votre contenu // Sauvegarde des informations calculées. cache_set('my_module_data', $my_data); } } return $my_data; }Dominique : Merci pour cette explication entre memecached et APC.
Pierre-Yves
août 27th, 2010 at 8 h 35 min
Bien le bonjour,

Génial, je me disais justement cette nuit qu’il me fallait trouver une solution pour les perf de mon site, car 80% des accès ne sont pas anonymes et les perf sont misérables…
Et là, ce matin, je me dis que ça fait longtemps que je ne suis pas venu ici et je tombe sur cet article
Très bon, même les commentaires le sont aussi, je ne regrette pas ma venue
A bientôt
CiaO ++
Optimiser le cache Drupal avec Boost et Memcache – modules indispensables | Le blog de Nouveaux Territoires
novembre 15th, 2010 at 16 h 30 min
[...] Je termine sur une ouverture avec l’api Drupal cache pour les développeurs http://juliendubreuil.fr/drupal/jouez-a-cache-cache-avec-drupal [...]
pounard
avril 5th, 2011 at 13 h 04 min
Juste histoire de contredire @Dominique ^^
APC est un OPCode cache, mais il propose aussi un mécanisme de cache binaire. Il peut donc s’apparenter à Memcache. Il existe le module http://drupal.org/project/apc qui ajoute le backend de cache binaire APC pour le core.
Sachant qu’APC tourne en RAM, mais aussi sur la même machine, et ne nécessite pas de passage au travers d’une couche TCP, il peut s’avérer incroyablement plus rapide que memcache. Avec Drupal 7 par exemple, il peut s’avérer intéressant de l’utiliser pour les caches petits néanmoins très fréquemment appelés tels que cache_menu et cache_bootstrap.
Guillem Canal
septembre 24th, 2011 at 21 h 44 min
Bel article, pour le stockage du cache je préfère le stockage memoire (si le serveur dispose d’assez de memoire) sinon le stockage fichier ; je n’ai jamais comprit la raison pour laquelle drupal stocke le cache en base, il ne devrait y avoir que des données métier… et de la configuration a la rigeur, mais rien de plus. (avis personnel)
Guillem Canal
septembre 24th, 2011 at 21 h 56 min
Petite precision, certaines pages peuvent par exemple contenir des informations utilisateur, rien ne vous empêche de mettre toute la page en cache et de créer des méthode pour peupler le/les blocs utilisateur avec Ajax.
Par exemple : Nous avons un bloc affichant des donnees utilisateur, dans la page mise en cache nous retrouvons juste un lien du style « Accéder à mon compte » et avec Ajax, on le peuple si l,utilisateur est connecté. Il suffit juste de jouer avec la session, de permettre le caching pour les utilisateurs enregistrés et d’indiquer a Drupal quels sont les pages a ne pas mettre en cache.
Boost, Authcache sont de bonnes pistes, mais je prefere faire ma tambouille de mon coté pour gagner en granularité.