# Redis qui explose en mémoire : diagnostiquer une politique maxmemory mal réglée

> Un cache d'objet Redis atteint sa limite mémoire et se met à rejeter des écritures. Diagnostic avec INFO memory et correctif par une politique d'éviction adaptée et un TTL par défaut.

- Auteur : Clément Hadrot
- Publié le : 2025-10-08
- Mis à jour le : 2025-10-08
- Catégorie : Performance
- URL : https://wpmoderne.dev.wordpress-developpement.fr/performance/redis-explose-memoire-maxmemory-diagnostic/

## L’essentiel

- Une erreur OOM côté Redis fait revenir WordPress à la base à chaque requête
- INFO memory révèle la fragmentation et la répartition réelle des clés
- allkeys-lru avec un TTL par défaut évite la récidive

Symptôme : un site de billetterie pour spectacles a vu son temps de réponse se dégrader brutalement un samedi soir, en pleine période de mise en vente d'un concert très demandé, alors que le cache d'objet Redis, censé absorber justement ce type de pic, semblait ne plus faire son travail. Les journaux PHP ne montraient rien d'anormal à première vue, mais le temps de réponse des pages avait doublé, comme si le cache d'objet n'existait plus.

## Symptôme approfondi : Redis qui rejette silencieusement les écritures

L'inspection des journaux Redis lui-même, plutôt que ceux de PHP, a révélé le vrai problème : le message `OOM command not allowed when used memory > 'maxmemory'` apparaissait des milliers de fois par minute. Redis avait atteint sa limite mémoire configurée (2 Go) et, avec la politique d'éviction par défaut sur cette installation (`noeviction`), refusait purement et simplement toute nouvelle écriture au lieu de faire de la place en supprimant d'anciennes clés. Chaque tentative de mise en cache d'un résultat de requête WordPress échouait donc silencieusement, obligeant WordPress à retourner à la base de données MySQL à chaque fois, exactement comme si le cache d'objet était absent.

## Diagnostic : INFO memory pour comprendre où va la mémoire

La commande `INFO memory` de Redis donne une vue détaillée de l'utilisation mémoire, y compris la fragmentation, qui s'est révélée être un facteur aggravant sur ce serveur.

```
$ redis-cli INFO memory
used_memory_human:1.98G
used_memory_rss_human:2.35G
mem_fragmentation_ratio:1.19
maxmemory_human:2.00G
maxmemory_policy:noeviction
evicted_keys:0
```

> L'essentiel à retenir : Une erreur OOM côté Redis fait revenir WordPress à la base à chaque requête ; INFO memory révèle la fragmentation et la répartition réelle des clés ; allkeys-lru avec un TTL par défaut évite la récidive

Le ratio de fragmentation de 1,19 signifiait que Redis réservait environ 19 % de mémoire supplémentaire par rapport aux données réellement utiles, un niveau qui reste dans une fourchette normale mais qui, combiné à une limite `maxmemory` déjà serrée par rapport au volume de données du site, suffisait à déclencher les rejets d'écriture bien avant que la mémoire physique du serveur ne soit réellement saturée.

## Deuxième diagnostic : quelles clés occupent le plus de place

La commande `MEMORY USAGE`, croisée avec un échantillonnage des clés via `SCAN` plutôt qu'un `KEYS *` qui aurait bloqué le serveur en production, a permis d'identifier que les clés liées aux résultats de recherche de spectacles, avec des combinaisons de filtres très nombreuses (ville, date, catégorie), représentaient à elles seules plus de 60 % de la mémoire utilisée, sans qu'aucune de ces clés n'ait de durée de vie (TTL) explicite : elles s'accumulaient indéfiniment depuis la mise en place du cache, des mois plus tôt.

```
$ redis-cli --scan --pattern 'wp_*_search_*' | wc -l
184230
$ redis-cli --scan --pattern 'wp_*_search_*' | head -1 | xargs redis-cli TTL
-1
```

Une valeur de `-1` pour le TTL confirme qu'une clé n'expire jamais, ce qui, cumulé sur des dizaines de milliers de combinaisons de recherche jamais réutilisées après leur première génération, expliquait la croissance continue de la mémoire jusqu'à la limite.

## Le correctif : politique d'éviction et TTL par défaut

Deux changements complémentaires ont été appliqués. D'abord, la politique `maxmemory-policy` a été changée de `noeviction` à `allkeys-lru`, qui supprime automatiquement les clés les moins récemment utilisées quand la limite mémoire est atteinte, plutôt que de rejeter les nouvelles écritures.

```
# redis.conf
maxmemory 2gb
maxmemory-policy allkeys-lru
```

Ensuite, et surtout, le code WordPress responsable de la mise en cache des résultats de recherche a été corrigé pour fixer systématiquement un TTL explicite, plutôt que de compter uniquement sur la politique d'éviction pour nettoyer a posteriori des clés qui n'auraient jamais dû s'accumuler sans limite de durée.

```
function obtenir_resultats_recherche_cache( $cle_filtre, $callback ) {
    $cache = wp_cache_get( $cle_filtre, 'recherche_spectacles' );
    if ( false !== $cache ) {
        return $cache;
    }

    $resultats = call_user_func( $callback );
    wp_cache_set( $cle_filtre, $resultats, 'recherche_spectacles', 15 * MINUTE_IN_SECONDS );

    return $resultats;
}
```

## Pourquoi les deux correctifs, et pas un seul

La politique `allkeys-lru` seule aurait résolu le symptôme immédiat (plus de rejet d'écriture), mais aurait laissé le cache expulser en continu des données potentiellement utiles pour faire de la place à des clés de recherche jamais réutilisées et pourtant conservées indéfiniment. Le TTL explicite seul aurait fini par régler le fond du problème, mais uniquement après plusieurs jours nécessaires pour que les anciennes clés sans expiration finissent par sortir naturellement du cache. Combiner les deux a réglé le symptôme immédiatement tout en corrigeant la cause à la racine.

- Consulter les journaux Redis eux-mêmes, pas seulement ceux de l'application, en cas de ralentissement inexpliqué du cache.
- `INFO memory` et un échantillonnage de clés révèlent où va réellement la mémoire.
- Un TTL explicite sur chaque type de donnée mise en cache évite l'accumulation silencieuse.

> Une politique `noeviction` n'est jamais un choix neutre : c'est un choix qui préfère la panne franche à la dégradation progressive, ce qui n'est pas toujours le bon compromis pour un site public.

## Hors périmètre

Cet incident et son correctif ne remettent pas en cause le choix initial de Redis face à Memcached pour ce projet, question tranchée séparément et pour d'autres raisons (structures de données, persistance optionnelle) qui restent valables indépendamment de ce réglage de politique mémoire.
