Cloudflare vient de dire tout haut ce que beaucoup pensent tout bas : ils ont économisé 100 téraoctets de mémoire en optimisant le cache DNS de 1.1.1.1. Pas en achetant de nouvelles instances. En supprimant le gaspillage. Si leur cache a pu perdre autant de gras, qu’est-ce que votre cluster Redis ou Valkey cache encore ?
Voilà la vérité qui dérange : dans la plupart des stacks SaaS que nous auditons, Redis consomme plus de mémoire en surcoût structurel qu’en données utiles. Le coupable : un cocktail de clés surdimensionnées, de charges JSON, de TTL laxistes et de politiques d’éviction qui laissent squatter les entrées froides. La bonne nouvelle, c’est que vous pouvez retirer 30–50 % de mémoire en 90 jours sans nuire à la latence ni au taux de hit—à condition de traiter la conception du cache comme un produit de première classe, pas comme une simple commodité.
Pourquoi votre cache est en surpoids (et pourquoi cela vous coûte plus que vous ne le pensez)
Redis et Valkey sont fulgurants, mais pas magiques. Sur un système 64 bits, chaque petit couple clé/valeur traîne un surcoût structurel qui dépasse largement la charge utile. Ordres de grandeur par clé :
- Entrée de dictionnaire : ~24 octets (pointeurs pour clé, valeur, suivant).
- En-tête(s) d’objet : ~16–24 octets selon le type/l’encodage.
- En-tête SDS + marge de l’allocateur pour la clé et la valeur : ~16–40 octets au total (selon la classe de taille).
- Gestion du TTL (si défini) : une autre entrée + métadonnées, facilement ~24–40 octets.
- Surcharges de l’allocateur et fragmentation : 10–30 % en plus.
Ce sont des chiffres directionnels, pas des constantes de labo certifiées. Mais le point reste le même : cette petite clé de 20 octets et cette valeur de 8 octets peuvent occuper 100–200+ octets en mémoire une fois que vous tenez compte des structures et des arrondis de l’allocateur. À des dizaines ou centaines de millions de clés, le gaspillage est énorme.
Ajoutez maintenant les péchés de plus haut niveau :
- Les objets JSON avec des clés entre guillemets et des noms de champs redondants gonflent les valeurs par 2–4× par rapport aux encodages binaires.
- Les clés composites qui répètent de longues chaînes de locataire ou d’espace de noms multiplient la duplication dans l’ensemble des données.
- Des TTL réglés sur « presque pour toujours » par « prudence » laissent les clés froides squatter indéfiniment.
- Des politiques d’éviction par défaut comme allkeys-lru ou random ne protègent pas vraiment le jeu chaud.
Rien de tout cela n’apparaît sur les tableaux de bord « le hit ratio a l’air correct ». Cela apparaît sur votre facture AWS et vos graphiques d’autoscaling.
Un cadre de décision pour CTO afin de retirer 30 %+ en 90 jours
Étape 1 : Faites l’inventaire et établissez une référence sérieuse
On n’optimise pas ce qu’on ne mesure pas. La première semaine, répondez à quatre questions avec des preuves, pas des intuitions :
- Quels sont les 10 préfixes de clés qui consomment le plus de mémoire ? Utilisez MEMORY USAGE en échantillonnage, redis-cli --memkeys, ou un échantillonneur basé sur SCAN pendant une heure canari. En cloud managé, exportez les métriques keyspace et bytes si disponibles.
- À quel point vos « petites valeurs » sont-elles petites ? Calculez les tailles p50/p90/p99 des clés et des valeurs par préfixe. Vous cherchez l’anti‑pattern où la médiane des valeurs est <64 octets et où le surcoût domine.
- Quel est votre jeu chaud vs jeu froid ? Suivez les clés uniques touchées par minute et leur distribution d’inter‑arrivées. Si 90 % du trafic touche 5–10 % des clés, vous pouvez être agressif sur l’éviction et les near‑caches.
- Où se cache la duplication ? Estimez le chevauchement de Jaccard des sous‑chaînes dans les clés (IDs de locataires, espaces de noms) et des champs dans les valeurs (chaînes répétées). Une forte répétition appelle l’interning/dictionnaire de compression.
Figez une baseline : octets totaux utilisés, octets par grand préfixe, taux de hit, latence p95, et distribution des durées de vie des clés (du set au dernier hit). Ce seront vos critères d’acceptation.
Étape 2 : Éliminez la duplication des clés par normalisation et interning
La plupart des clés composites transportent le même bagage un million de fois. Exemple : tenant:abc123:region:us-east-1:resource:xyz. Remplacez les sous‑chaînes répétées par des IDs compacts :
- Assignez des IDs entiers aux locataires, régions et autres espaces de noms répétés. Conservez un petit dictionnaire faisant autorité dans le cache (ou votre base primaire) mappant ID → chaîne.
- Réordonnez les parties de la clé du plus au moins variant (p. ex. t:123|r:1|xyz). Cela améliore la répartition dans la table de hachage et vous aide à repérer des opportunités de sharding.
- Si vous devez garder des clés lisibles pour l’exploitation, ajoutez un service sidecar keymap pour le débogage qui ré‑étend les IDs—n’alourdissez pas chaque clé en production avec des chaînes complètes.
Attendez‑vous à 10–30 % d’économies rien que sur les clés dans des systèmes multi‑locataires. Oui, cela demande une migration. Oui, ça vaut le coup.
Étape 3 : Arrêtez de mettre en cache du JSON sauf obligation absolue
JSON est parfait pour les logs et les humains ; c’est mauvais pour la mémoire chaude. Passez à un schéma binaire compact et stable :
- MessagePack si vous voulez un remplacement multi‑langage simple avec un minimum d’efforts.
- Protocol Buffers si vous voulez des schémas explicites, de la compatibilité ascendante/descendante, et les tailles de messages les plus faibles pour des objets riches en scalaires.
- FlatBuffers/Cap’n Proto si l’accès zéro‑copie ou des budgets CPU extrêmement serrés comptent.
Chiffres réels : sur une plateforme e‑commerce que nous avons aidée, les objets de session faisaient en moyenne 680 octets en JSON. Proto a réduit à 210 octets sans changer les champs. La latence s’est aussi améliorée car le parsing coûte moins cher.
Astuce spécifique à Redis/Valkey : regroupez plusieurs champs liés dans un hash qui reste dans l’encodage compact listpack (petits champs, peu d’entrées). Cela effondre le surcoût par champ en une seule allocation et peut battre une unique chaîne binaire pour de petites structures—testez les deux sur vos données.
Étape 4 : Compressez de façon sélective là où ça compte
Si vous mettez en cache des charges textuelles ou des embeddings, la compression paie—mais pas pour tout. Politique pratique :
- Ne compressez que les valeurs ≥ 512–1 024 octets. En‑dessous, la marge de l’allocateur et le coût CPU annulent souvent le gain.
- Utilisez zstd à un niveau faible (1–3). Pour des schémas répétés (descriptions produit, prompts), fournissez un dictionnaire spécifique au domaine pour un gain supplémentaire de 10–20 %.
- Faites la compression dans l’application avant l’écriture, afin de contrôler le compromis et de garder les jeux chauds non compressés.
Suivez le temps de décompression dans le même histogramme que la latence de hit du cache. Si la p95 dépasse votre SLO, revenez en arrière ou relevez le seuil de déclenchement.
Étape 5 : Dimensionnez correctement les TTL avec des données, pas avec la peur
Le TTL est votre seul « ramasse‑miettes » garanti. Utilisez‑le. Voici une approche posée que presque aucune équipe ne suit :
- Calculez la distribution du temps jusqu’au dernier hit par type de clé. Si 85 % des clés ne sont jamais retouchées après 10 minutes, un TTL de 60 minutes est de l’auto‑sabotage.
- Appliquez des TTL différenciés par préfixe en fonction de la valeur métier et du coût de rafraîchissement. Le préfixe coûteux et chaud obtient un TTL plus long ; les préfixes peu coûteux et rarement touchés se mesurent en minutes, pas en heures.
- Ajoutez une décroissance de TTL pour les clés sous‑performantes : en cas de miss après éviction, définissez un TTL plus bas sauf si l’usage repart à la hausse. Cela évite de réchauffer indéfiniment le jeu froid.
En pratique, la seule discipline sur les TTL génère souvent 10–20 % de soulagement mémoire en une semaine, sans autre changement de code qu’une configuration setex/pipelining.
Étape 6 : Choisissez une politique d’éviction qui protège le jeu chaud
La plupart des clusters tournent discrètement en allkeys-lru ou pire, en noeviction. Si vos accès suivent une distribution déséquilibrée (c’est le cas), LFU bat LRU pour retenir les clés chaudes pendant les bouffées de trafic. Passez à allkeys-lfu pour les caches fourre‑tout et à volatile-lfu quand vous vous fiez au TTL pour la durée de vie des objets.
Surveillez les entrées fantômes—des clés qui tournent vite et évinceraient des éléments chauds stables. Si vous voyez cela, ajoutez un near‑cache dans l’app (Étape 7) ou une politique d’admission (ne pas admettre au premier accès) pour filtrer les one‑hit wonders.
Étape 7 : Ajoutez un near‑cache applicatif pour filtrer les doublons
Un cache en processus de 50–200 Mo par instance d’app peut protéger votre cache central contre les doublons en rafale et réduire la latence de bout en bout :
- Utilisez Caffeine (JVM) ou Ristretto (Go) avec admission TinyLFU et éviction W‑TinyLFU. Ces politiques réduisent drastiquement la pollution du cache par des clés à usage unique.
- Définissez un TTL court (30–120 s) et dimensionnez‑le pour tenir confortablement en L3. L’objectif est d’intercepter les bourrasques, pas de tout stocker.
- Enregistrez une métrique : Lectures Redis évitées. Si vous ne voyez pas 20–40 % de lectures backend évitées au pic, ajustez l’admission et la taille.
Cette seule étape réduit couramment de 10–25 % l’empreinte de votre cache central en vous permettant de raccourcir les TTL centraux et de pousser LFU plus agressivement.
Étape 8 : Optimisez le moteur que vous avez déjà
Après les changements de modèle de données, pressez l’allocateur et les structures :
- Activez la défragmentation active dans Redis/Valkey pour les clusters long‑courriers sujets au churn. Cela ne corrige pas les erreurs de conception, mais récupère la fragmentation avec le temps.
- Évaluez le tuning de jemalloc là où c’est disponible : threads en arrière‑plan et purge des pages sales réduisent les pics de RSS et le gaspillage de l’allocateur.
- Consolidez de nombreux petits champs dans des hashes qui restent sous les seuils listpack. Évitez de passer en tables de hachage sauf si vous avez vraiment besoin d’objets volumineux.
- Pipelinez MGET/MSET et regroupez les écritures. Moins d’allocations, moins d’allers‑retours, CPU plus bas, latence de queue réduite.
Étape 9 : Segmentez le cache : niveaux chaud et froid
Tous les caches ne méritent pas le même silicium. Scindez votre dataset :
- Niveau chaud : instances plus petites, CPU plus rapides, TTL plus bas, LFU activé. Pensez 10–30 % des clés générant 80–90 % des hits.
- Niveau froid : instances avec plus de RAM, CPU plus lents, TTL plus longs pour les éléments coûteux à recalculer mais peu accédés.
Routage par préfixe. En services managés, cela vous permet d’ajuster la classe d’instance plutôt que d’acheter un « taille unique qui ne va à personne ». En self‑host, épinglez les processus du niveau chaud à des cœurs et surveillez la contention sur les lignes de cache.
Conséquences sur le coût, la latence et le rayon d’impact
Chaque Go éliminé vous paie trois fois :
- Coût direct : moins de nœuds et/ou des classes inférieures. Beaucoup d’équipes soutiennent des baisses de coût de 20–40 % après l’amincissement, puis réinvestissent une fraction dans des fréquences CPU plus hautes ou un stockage plus rapide pour améliorer la latence de queue.
- Latence : des ensembles de travail plus petits tiennent mieux dans les caches CPU. Les near‑caches applicatifs retirent 0,5–2 ms aux requêtes chaudes en évitant le saut réseau.
- Rayon d’impact : des caches plus minces redémarrent plus vite, répliquent moins et basculent avec un état plus petit. Vos alertes à 3 h du matin durent moins longtemps.
Vous n’aurez pas la manchette des 100 To de Cloudflare. Vous obtiendrez quelque chose de plus précieux pour une startup : de la marge sans héroïsme.
Plan sur 90 jours (qui sort vraiment)
Jours 1–30 : Référence et gains rapides
- Instrumentez : histogrammes de taille par préfixe, temps jusqu’au dernier hit, analyse des sous‑chaînes dupliquées dans les clés.
- Passez à LFU là où c’est pertinent et activez la défrag active.
- Corrigez les TTL pour les pires contrevenants (p. ex. de 60 min → 10 min pour des clés dont 95 % des derniers hits sont < 5 min).
- Ajoutez un near‑cache applicatif avec admission TinyLFU dans un service à fort trafic. Test A/B pendant une semaine.
Jours 31–60 : Changements de schéma et d’encodage
- Introduisez MessagePack/Protobuf pour les deux préfixes les plus gourmands en mémoire. Gardez JSON seulement en bordure si vous y êtes contraints.
- Normalisez les clés avec interning d’IDs pour locataires/régions. Fournissez un chemin d’expansion réversible pour le debug.
- Compressez sélectivement (>= 1 Ko) avec zstd niveau 1–3 et des dictionnaires de domaine. Protégez via feature flag et moniteurs de SLO.
- Choisissez stockage hash vs string par objet sur la base de microbenchmarks avec vos charges réelles. Gravelez la décision dans votre DATA.md ou équivalent.
Jours 61–90 : Segmentation et remise à plat de la capacité
- Scindez les niveaux chaud et froid par préfixe. Déplacez les ensembles coûteux‑mais‑froids vers un niveau plus grand et moins cher ; gardez le cœur chaud petit et rapide.
- Rejouez les baselines. Cible : réduction mémoire de 30 %+, latence p95 stable ou meilleure, taux de hit inchangé ou en hausse.
- Redimensionnez les instances ou retirez un réplica si la sécurité de réplication le permet. Préparez les plans de rollback et de réchauffage.
- Codifiez un Cache Contract : ownership du schéma, budgets mémoire par préfixe, politique de TTL, et une règle d’admission (« nous ne mettons pas en cache ce qui n’est pas réutilisé »). Les PRs qui modifient le comportement du cache doivent le mettre à jour.
Pièges et arbitrages
- La compression n’est pas gratuite. Si votre chemin chaud est déjà lié par le CPU, laissez les jeux chauds non compressés et ne compressez que les valeurs froides ou volumineuses. Mesurez p95 et p99.
- Les encodages binaires changent l’ergonomie opérationnelle. Ajoutez des outils pour pretty‑printer les valeurs en staging. Ne revenez pas à JSON « juste pour déboguer ».
- La normalisation des clés complique l’inspection ad hoc. Résolvez‑le avec un sidecar keymap ou un outil CLI, pas en alourdissant les clés de production.
- LFU a besoin d’apprentissage. Au déploiement, attendez une courte période d’adaptation. Utilisez des scripts de warm‑up ou un pré‑remplissage si le cold start compte.
Pourquoi c’est d’actualité maintenant
Trois signaux industriels ont convergé ce trimestre :
- Le gain de 100 To de Cloudflare a remis l’austérité mémoire à la mode. Et c’est justifié. Le Go le moins cher est celui que vous n’allouez pas.
- La résistance d’Android à l’usage mémoire de l’IA rappelle à tous que la RAM est le véritable goulot d’étranglement des apps modernes—sur l’appareil comme dans votre data plane.
- La consolidation des vendeurs autour des hubs de modèles et de l’infra rend les tarifs de burst et les quotas très réels. Vous ne pourrez pas acheter éternellement votre sortie d’un mauvais design de cache.
Les équipes qui traitent les caches comme des produits—schémas, contrats, SLOs, budgets—iront plus vite et dépenseront moins en 2026. Celles qui ne le font pas continueront d’ajouter des nœuds jusqu’à payer un vrai salaire en déchets mémoire.
Où le nearshore s’insère
L’amincissement du cache est un projet nearshore idéal : levier élevé, mesurable et borné. Un pod basé au Brazil peut travailler avec vos staff engineers pendant 6–8 heures de recouvrement horaire, construire les baselines, livrer les changements d’encodage derrière des flags et vous remettre un playbook de réduction de capacité. Vous gardez le code et les gains.
Points clés
- Le surcoût domine dans Redis/Valkey. De petites clés/valeurs consomment souvent 5–10× leur charge utile en mémoire.
- Normalisez les clés et abandonnez JSON. Les encodages binaires plus l’interning d’ID livrent couramment 20–40 % d’économies.
- Des TTL pilotés par la donnée et LFU. Protégez le jeu chaud et laissez mourir vite les clés froides.
- Ajoutez un near‑cache TinyLFU dans les apps pour filtrer les doublons et réduire la pression sur le cache central de 10–25 %.
- Segmentez niveaux chaud/froid et redimensionnez les instances. Attendez‑vous à 30 %+ de réduction mémoire en 90 jours sans dégrader la latence.