« Memcache » est un service autonome permettant de stocker tous types de variables, objets, etc. Mais principalement de mettre en cache les réponses des bases de données, en gérant les données et les objets en mémoire de façon à réduire le nombre d’appel à cette ou ces ressources.
Comment fonctionne « memcache »
« Memcache » est un « moteur » qui stocke des valeurs sérialisées par des clefs prédéfinies par le développeur.
Exemple avec MySQL : votre code doit vérifier si vos données sont déjà stockées en cache avant de faire une requête MySQL en base. Si votre clef s’y trouve, vous évitez ainsi une connexion, gourmande en termes de ressources, avec la base de données.
Exemple de code :
$myCache = new Memcache; $myCache->addServer('MEMCACHE_HOST', MONPORT); $myCacheKey = 'ma_clef'; $myData = $myCache->get($myCacheKey); if (!$myData) { $db = mysql_connect('MON_SERVEUR_MYSQL', 'LOGIN', 'PASSWORD'); mysql_select_db('MA_BASE',$db) ; $sql = "SELECT * FROM matable WHERE id = :id"; $req = mysql_query($sql) or die('erreur mysql'); $data = mysql_fetch_all($req); $myCache->set($myCacheKey, serialize($data)); mysql_close(); }
Comment installer « memcache »
Installer le service « memcache »
sudo apt-get install memcached libmemcached-tools
Nous allons maintenant créer des sockets (« memcache » semble plus performant grâce aux sockets et cela permet de contrôler l’utilisation du cache beaucoup plus facilement).
sudo cp /etc/init.d/memcached /etc/init.d/memcached.bak sudo vi /etc/init.d/memcached
Remplacez le script de démarrage par celui conseillé par la communauté drupal ; vous verrez qu’il permet la création de 13 sockets (http://drupal.org/node/1181968#comment-5855430) :
#! /bin/sh # # file: /etc/init.d/memcached # # chkconfig: - 55 45 # description: The memcached-multi daemon is a network memory cache service. This script starts # memcached specifically for Drupal through Unix sockets (13 socket streams) # processname: memcached-multi # pidfile: /var/run/memcached/memcached.*.pid # # adapted from http://drupal.org/node/1181968 # by Coert van Gemeren ([email protected]) on 2012-03-30 PORT=11211 UDP=0 SOCKET=/tmp/memcached.socket VAR=0 USER=memcached MAXCONN=300 CACHESIZE=64 MEMCACHED=/usr/bin/memcached RETVAL=0 prog="memcached" start_instance() { if [ ! -e /var/run/memcached/memcached.$1.pid ] ; then echo "Starting $prog ($1)" OPTIONS="-d -s $3 -a 766 -L -t 8 -u $USER -m $2 -c $MAXCONN -P /var/run/memcached/memcached.$1.pid" start-stop-daemon --start --quiet --pidfile /var/run/memcached/memcached.$1.pid --make-pidfile --exec $MEMCACHED -- $OPTIONS RETVAL=$? [ $RETVAL -eq 0 ] && touch /var/lock/memcached/$1 else echo "$prog ($1) already running: /var/run/memcached/memcached.$1.pid" fi } stop_instance() { if [ -e /var/run/memcached/memcached.$1.pid ] ; then PID=$(ps -ef|grep "/var/run/memcached/memcached.$1.pid"|grep -v "grep"|awk '{print $2}') # FIXME! Not working yet # PID=$(cat /var/run/memcached/memcached.$1.pid) echo "Stopping $prog ($1)" # FIXME! Not working yet # start-stop-daemon --stop --oknodo --pidfile /var/run/memcached/memcached.$1.pid --signal KILL --exec $MEMCACHED kill -9 $PID RETVAL=$? if [ $RETVAL -eq 0 ] ; then rm -f /var/lock/memcached/$1 rm -f /var/run/memcached/memcached.$1.pid fi else echo "$prog ($1) not running: /var/run/memcached/memcached.$1.pid" fi } start() { # insure that /var/run/memcached has proper permissions if [ "`stat -c %U /var/run/memcached`" != "$USER" ]; then chown $USER /var/run/memcached fi # we start 13 socket streams for memcached start_instance default 64 /tmp/memcached.socket0; start_instance block 64 /tmp/memcached.socket1; start_instance content 64 /tmp/memcached.socket2; start_instance field 64 /tmp/memcached.socket3; #Field is new in D7, it is not yet supported in the memcache module reports page start_instance filter 64 /tmp/memcached.socket4; start_instance form 64 /tmp/memcached.socket5; start_instance menu 64 /tmp/memcached.socket6; start_instance page 64 /tmp/memcached.socket7; start_instance update 64 /tmp/memcached.socket8; start_instance views 64 /tmp/memcached.socket9; start_instance session 64 /tmp/memcached.socket10; start_instance users 64 /tmp/memcached.socket11; start_instance pbold 64 /tmp/memcached.socket12; } stop () { stop_instance default; stop_instance block; stop_instance content; stop_instance field; stop_instance filter; stop_instance form; stop_instance menu; stop_instance page; stop_instance update; stop_instance views; stop_instance session; stop_instance users; stop_instance pbold; } restart () { stop start } # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status memcached ;; restart|reload|force-reload) restart ;; *) echo $"Usage: $0 {start|stop|status|restart|reload|force-reload}" exit 1 esac exit $?
Comment installer l’extension PHP « memcache »
sudo apt-get install php5-dev php-pear make sudo pecl install memcache sudo vi /etc/php5/conf.d/memcache.ini
Important : ne pas installer l’extension « memcached » (extension plus récente que « memcache »). (Voir plus bas « les détails de la configuration » pour plus d’explications)
Ajouter les lignes suivantes :
[memcache] ; Data will be transferred in chunks of this size memcache.chunk_size = 32768 memcache.hash_strategy = consistent memcache.default_port = 0 session.save_handler = memcache session.save_path = "unix:///tmp/memcached.socket11:0?persistent=1&weight=1&timeout=1&retry_interval=15"
Pour plus explications sur les directives voir : http://www.php.net/manual/fr/memcache.ini.php
Il ne vous reste plus qu’à démarrer le service « memcache » et « apache » pour prendre en compte l’extension PHP « memcache ».
sudo /etc/init.d/memcached restart sudo /etc/init.d/apache2 restart
Installer le module « memcache » pour drupal
Aller sur le module « memcache » de drupal. http://drupal.org/project/memcache
Télécharger la dernière version compatible pour drupal 7.
Copier le répertoire « memcache » dans le répertoire de vos modules contribués.
Allez sur la page d’installation des modules (admin/modules).
Cochez les deux modules ci-dessous :
Ensuite, il faut modifier votre fichier sites/default/settings.php :
$conf += array( 'memcache_extension' => 'Memcache', 'show_memcache_statistics' => 0, 'memcache_persistent' => TRUE, 'memcache_stampede_protection' => TRUE, 'memcache_stampede_semaphore' => 15, 'memcache_stampede_wait_time' => 5, 'memcache_stampede_wait_limit' => 3, 'memcache_key_prefix' => basename(realpath(conf_path())), ); // We don't use chained memcached caching for sites cron, php-cli and install! // memcache-session.inc has not successfully been ported to D7 yet! if (isset($_SERVER['HTTP_USER_AGENT']) && !preg_match("/(?:cron\.php|install\.php)/", $_SERVER['REQUEST_URI'])) { if (isset($_SERVER['HTTP_HOST'])) { $conf += array( 'cache_inc' => './sites/all/modules/contrib/memcache/memcache.inc', 'lock_inc' => './sites/all/modules/contrib/memcache/memcache-lock.inc', 'memcache_servers' => array( 'unix:///tmp/memcached.socket0' => 'default', 'unix:///tmp/memcached.socket1' => 'block', 'unix:///tmp/memcached.socket2' => 'content', 'unix:///tmp/memcached.socket3' => 'field', 'unix:///tmp/memcached.socket4' => 'filter', 'unix:///tmp/memcached.socket5' => 'form', 'unix:///tmp/memcached.socket6' => 'menu', 'unix:///tmp/memcached.socket7' => 'page', 'unix:///tmp/memcached.socket8' => 'update', 'unix:///tmp/memcached.socket9' => 'views', 'unix:///tmp/memcached.socket10' => 'session', 'unix:///tmp/memcached.socket11' => 'users', ), 'memcache_bins' => array( 'cache' => 'default', 'cache_block' => 'block', 'cache_content' => 'content', 'cache_field' => 'field', 'cache_filter' => 'filter', 'cache_form' => 'form', 'cache_menu' => 'menu', 'cache_page' => 'page', 'cache_update' => 'update', 'cache_views' => 'views', 'session' => 'session', 'users' => 'users' ), ); } } include_once('./includes/cache.inc'); // FIXME! We have to literally include memcache.inc already at this point // otherwise D7 won't start (why? probably because php session.save_handler is memcached, but not sure) include_once('./sites/all/modules/contrib/memcache/memcache.inc'); $conf['cache_backends'][] = 'sites/all/modules/contrib/memcache/memcache.inc'; $conf['cache_default_class'] = 'MemCacheDrupal'; //Any other cache bin goes to memcached
Détail de la configuration
La variable « memcache_extension » : permet de choisir l’extension PECL désirée (« memcache » ou « memcached », notez l’ajout d’un d), l’extension « memcached » est plus récente et ajoute de nouvelles fonctionnalités. Cependant, l’extension « memcache » parait plus stable pour drupal et elle est préconisée par la communauté. Certains tickets sur drupal remontent des effets bords avec l’utilisation de « memcached ». Nous recommandons donc de suivre la communauté et d’utiliser l’extension « memcache ».
- show_memcache_statistics : permet d’afficher des statistiques sur l’utilisation de « memcache » directement en bas des pages ; il faut mettre cette valeur à 0 ; vous pourrez de toute manière l’activer, pour un debuggage, directement depuis le backoffice drupal.
- memcache_persistent : par défaut TRUE, la connexion ne sera pas fermée à la fin de l’exécution du script et par la fonction Memcache::close().
- memcache_stampede_protection : lors de l’expiration du cache, stampede protection (littéralement protection d’une fuite en désordre) permet à un process d’envoyer l’ancien cache pendant qu’un autre process génère le nouveau cache. Cela permet donc d’avoir des performances accrues.
- memcache_stampede_semaphore : valeur en secondes, permet d’éviter de mettre de nouveau en cache une même valeur pendant le laps de temps défini.
- memcache_stampede_wait_time : valeur en secondes, temps d’attente maximum pendant la mise en cache.
- memcache_stampede_wait_limit : valeur en entier, nombre d’essai maximum lors de la mise en cache.
IMPORTANT : « wait_time * wait_limit » doit toujours être inferieur au timeout du serveur web (pour apache et par défaut, le timeout serveur est de 30 secondes).
- memcache_key_prefix : préfixe pour les clefs lors de la mise en cache d’une valeur, obligatoire lors d’une utilisation multi sites.
- cache_inc : chemin vers le fichier memcache.inc du module « memcache ».
- lock_inc : chemin vers le fichier « lock.inc » du module « memcache ».
- memcache_servers : on indique nos 12 process « memcache » correspondants aux « bins memcache » ; vous devez mettre les mêmes valeurs définies dans le daemon 3.a).
Exemple :
'unix:///tmp/memcached.socket0' => 'default', 'unix:///tmp/memcached.socket1' => 'block', 'unix:///tmp/memcached.socket2' => 'content', 'unix:///tmp/memcached.socket3' => 'field', 'unix:///tmp/memcached.socket4' => 'filter', 'unix:///tmp/memcached.socket5' => 'form', 'unix:///tmp/memcached.socket6' => 'menu', 'unix:///tmp/memcached.socket7' => 'page', 'unix:///tmp/memcached.socket8' => 'update', 'unix:///tmp/memcached.socket9' => 'views', 'unix:///tmp/memcached.socket10' => 'session', 'unix:///tmp/memcached.socket11' => 'users',
- memcache_bins : on indique à nos caches drupal, la connexion aux bins préalablement renseignés dans la variable memcache_servers.
Normalement les bins les plus utilisés sont : default, filter et menu.
IMPORTANT : Si le module « drupal memcache » n’arrive pas à se connecter au socket unix et à l’extension PECL Memcache, je vous conseille d’appliquer le patch disponible dans cette « issue »: http://drupal.org/node/1690130
Statistique d’utilisation de « memcache »
Maintenant que votre site utilise « memcache », vous devez vérifier que tout fonctionne correctement et que votre configuration est optimale ; pour cela aller sur la page :
http://monsite/admin/reports/memcache
Vous retrouvez ici vos 12 process « memcache » (1 par onglet).
Vérifiez que la mémoire allouée est suffisante pour chaque process :
Vérifiez également si vos variables sont bien cachés :
Notre avis
Drupal peut-être très gourmand en requêtes SQL surtout si votre site n’est pas optimisé. « memcache » ne corrige pas ce problème d’optimisation mais permet de limiter les appels à MySQL.
Pour information, l’utilisation de socket n’est pas obligatoire mais permet très rapidement de voir ce qui est mis en cache ou non, directement depuis le backoffice DRUPAL ; vous pouvez ainsi améliorer votre système de cache en conséquence.