1 points par GN⁺ 4 시간 전 | 1 commentaires | Partager sur WhatsApp
  • Un cache est introduit pour réduire la charge de la base de données, mais un outil facile à utiliser comme Redis tend avec le temps à devenir une dépendance assimilée à un stockage persistant
  • Le problème n’est pas la fonctionnalité de persistance de Redis, mais le fait qu’un composant introduit au départ comme cache volatil se retrouve mêlé à l’état central de l’application dans les opérations courantes
  • memcached est défini officiellement comme un système de cache d’objets en mémoire distribué et ne repose pas sur le stockage disque, ce qui le rend plus facile à traiter comme une charge de travail de cache sans état
  • Avec plusieurs instances memcached, ce n’est pas le serveur mais le client qui répartit les données à partir d’une liste d’URL et du hachage des clés ; les nœuds en panne sont retirés du hachage puis des tentatives de reconnexion sont effectuées plus tard
  • Avant d’ajouter un cache sous prétexte que « la base de données est lente », il faut d’abord vérifier les requêtes lentes et les index manquants

Le moment où Redis passe de cache à stockage

  • Quand on exploite une infrastructure, la demande « il nous faut un cache » revient souvent, et Redis vient facilement à l’esprit en premier parce qu’il est familier et riche en fonctionnalités
  • La page d’accueil de Redis met désormais en avant Redis Iris, un moteur de contexte temps réel pour les applications d’IA, mais c’est une orientation compréhensible pour une entreprise qui doit générer des revenus
  • Une fois Redis déployé et la chaîne de connexion fournie, il se comporte au départ comme un cache fiable

Les problèmes qui apparaissent quelques mois plus tard

  • Avec le temps, comme cache.set("key", "value") est bien plus simple que INSERT INTO table VALUES ('key', 'value'), les gens commencent à traiter Redis de la manière suivante
    • comme un composant toujours présent, un endroit où conserver les données ; de fait, une base de données
    • REmote DIctionary Server finit par être perçu comme un stockage permanent plutôt que comme un cache volatil
  • Ni vous ni vos collègues de l’équipe d’exploitation ne vous en rendez compte, et comme vous pensez que tout le monde considérera le cache comme volatil, le système d’alerting ne le détecte pas non plus
    • Le problème ne devient visible qu’au moment d’une mise à niveau, d’un déplacement de nœud, ou d’un incident comme un tiroir de disque dur RAID0 qui se détache du serveur Redis
  • Le vrai problème n’est pas que Redis dispose ou non de fonctions de persistance, mais le décalage entre l’hypothèse d’un Redis introduit comme cache et le fait que les gens ne le traitent pas comme un cache
  • Lorsqu’on découvre cette dépendance trop tard, Redis est déjà trop profondément imbriqué dans l’application pour être retiré facilement, et il faut finalement le maintenir et le superviser comme un « animal de compagnie »

Pourquoi memcached remplit plus directement le rôle de cache

  • memcached est un « système gratuit, open source, haute performance, de cache d’objets en mémoire distribué », un cache générique conçu pour accélérer les applications web dynamiques en réduisant la charge sur la base de données
  • Dans des frameworks qui prennent en charge le cache enfichable comme Django, il est possible de changer de backend de cache
  • Même s’il offre moins de fonctionnalités que Redis, memcached peut être préférable parce que ses caractéristiques d’exploitation sont plus simples
    • La gestion des indisponibilités est facile : les bibliothèques clientes ignorent souvent les exceptions de connexion, et un simple get peut renvoyer une valeur par défaut ou None même si le serveur est hors service
    • memcached n’intègre pas de fonctionnalité de cluster, ce qui rend paradoxalement le clustering plus pratique
      • Il suffit de configurer plusieurs URL dans la bibliothèque cliente pour qu’elle choisisse l’instance cible via le hachage des clés
      • Si un appel client détecte la panne d’une instance, il retire le nœud du hacheur puis tente automatiquement une reconnexion après un certain délai
    • La contrainte de persistance est structurellement réduite : memcached n’écrit pas sur disque, ce qui facilite son ordonnancement n’importe où comme charge de travail sans état
  • On peut construire un mode d’exploitation similaire avec Redis, mais l’architecture de memcached est plus proche de cette logique, ce qui le rend plus intuitif à traiter comme un cache
  • memcached reste une application relativement simple, et le fait qu’il y ait presque aucun surcoût même avec des dizaines d’instances de cache d’environ 64 Mo constitue un argument de choix
  • Beaucoup de problèmes du type « la base de données est lente » proviennent en réalité de requêtes lentes ou d’index manquants ; il faut donc examiner aussi l’optimisation des requêtes en parallèle de l’ajout d’un cache
  • Si les choix de conception de memcached vous intéressent, le blog memcached contient de nombreux billets passionnants, dont celui publié en mai : « Combien de temps cette réponse prend-elle vraiment ? (How Long Does That Response Take… For Real?) »

1 commentaires

 
GN⁺ 4 시간 전
Commentaires sur Hacker News
  • Redis est une excellente technologie, mais j’ai l’impression qu’il a du mal à bien remplir en même temps deux rôles différents : celui de structure de données persistante et celui de cache volatil
    En pratique, même dans Redis, les deux se mélangent mal, puisque la persistance s’active ou se désactive globalement
    Pour un cache pur, j’utiliserais memcached ou un équivalent, et je ne prendrais Redis avec la persistance activée que lorsqu’il faut des structures de données comme des tableaux de scores
    Chez $WORK, nous n’avons adopté ni l’un ni l’autre, et pour la couche de cache des tâches lentes, nous stockons les données à la fois dans le système de fichiers et dans une table de base de données utilisée comme magasin clé-valeur
    La DB aide à coordonner le thundering herd, les lectures depuis le même serveur ne touchent que le système de fichiers, et les lectures depuis d’autres serveurs consultent une fois la DB puis conservent les données dans le système de fichiers
    On pourrait remplacer la couche système de fichiers par memcached, mais jusqu’ici cela fonctionne extrêmement bien

    • Après avoir utilisé Memcachedb (memcache + bdb pour la persistance) à la fin des années 2000, je suis arrivé à pratiquement la même conclusion
      Redis avait clairement plus de fonctionnalités, et antirez était quelqu’un de charismatique tout en étant étonnamment humble, donc je comprends pourquoi Redis est devenu plus populaire
      Malgré tout, pour moi, memcached a toujours été l’exemple ultime de choisir une technologie ennuyeuse
      En tant qu’ingénieur plateforme, je peux prendre en charge les deux, mais quand des développeurs commencent à utiliser les fonctionnalités avancées de Redis comme la persistance, la réplication ou le clustering, j’essaie de vérifier qu’ils comprennent bien les inconvénients de ce choix
    • Rien qu’en utilisant une table de DB comme magasin clé-valeur avec un peu de système de fichiers à côté, on peut déjà faire énormément de choses avant de payer le coût de mise en place d’un stockage de cache dédié
      Chaque fois que je propose ce genre de solution, je me suis retrouvé d’innombrables fois à me battre sur le terrain avec des gens inexpérimentés qui estiment qu’un cache doit absolument vivre dans un stockage dédié
  • Dire « memcache » n’évite absolument pas ce genre de problèmes
    Au milieu des années 2000, j’ai travaillé sur un système extensible qui utilisait memcache, et les développeurs sont tombés exactement dans les mêmes pièges que ceux cités dans l’article pour Redis
    Ils ont essayé de contourner les lois des systèmes distribués avec memcache, et à cause de l’addiction au cache, ils dimensionnaient la flotte de serveurs en partant du principe que memcache serait toujours disponible, si bien qu’en cas de panne cela explosait soudainement comme une attaque DDoS
    Il y avait aussi de l’amplification d’écriture : lorsqu’un hôte purgeait une clé à fort TPS, tous les autres hôtes frappaient le service dépendant pour la regénérer, les hot keys créaient des hot hosts, et memcached tournait sur les mêmes machines que les démons de service, ce qui menait à des pics CPU mystérieux
    Des appels memcache partaient aussi dans un trou noir à cause de la persistance d’anciennes entrées DNS
    Tout cela aurait pu être évité avec un meilleur usage de memcache, mais la tentation de l’abus était trop forte

  • J’ai l’impression d’avoir vu en production presque tous les problèmes Redis/Valkey mentionnés par l’auteur
    Il y a eu un incident où Valkey, faute de politique mémoire, a consommé toute la mémoire et provoqué des erreurs d’écriture de l’append-only file, et un autre où les écritures AOF ont échoué simplement parce que le disque était plein
    Il est aussi arrivé que Redis soit vivant, en cours d’exécution et entièrement rempli de données utilisateur, et comme rien ne permettait de revenir vers un chemin lent, cela générait des erreurs 500
    J’ai aussi vu des cas où des gens utilisaient de manière créative des sorted sets et d’autres structures de données tout en dépendant du fait que cet ensemble ne serait jamais évincé
    Malgré ce retour d’expérience, il me semble encore difficile de recommander memcache avant Redis
    Concevoir l’application pour qu’elle ait une disposition de cache compatible avec memcache peut être délicat, et si une équipe suffisamment grande utilise memcache, il est très probable qu’elle finisse par trouver une voie qui nécessite Redis
    On se retrouve alors à maintenir 2 technologies de cache

    • Dès que quelqu’un décide d’utiliser Redis pour autre chose que du cache, on a de fait 2 technologies de cache
      Une instance Redis configurée pour le cache ne peut pas servir à d’autres usages, une instance de cache doit avoir de l’éviction, et une instance non dédiée au cache ne doit pas en avoir
      Au final, il faut une deuxième instance Redis avec une configuration différente
      Honnêtement, concevoir une application avec une disposition de cache compatible memcache revient au même que la concevoir avec une disposition de cache compatible Redis
      Les patterns de cache applicatif sont les mêmes : on récupère, et si la valeur n’existe pas, on la calcule puis on l’écrit
    • S’il n’existe pas déjà, je créerais une interface d’abstraction permettant de demander une clé tout en passant une fonction asynchrone ou une lambda qui récupère la valeur depuis la source en cas de cache miss
      var value = cache.lookup( keyname, () => db.query(...), TimeSpan.FromMinutes(5) // or CacheOptions );
      De cette façon, on peut soit basculer immédiatement vers le chemin de repli, soit insérer la valeur au moment du cache miss
    • Ne pas maintenir 2 technologies de cache est toujours un argument gagnant
  • Une autre caractéristique peu mentionnée de memcache est que toutes les opérations sont en O(1) par conception
    C’est un choix d’architecture délibéré des auteurs, qui impose des contraintes, mais garantit qu’il n’y aura pas d’arrêts aléatoires sur des opérations simples
    Redis, à l’inverse, avec son cœur mono-thread, peut exécuter des opérations d’une complexité arbitraire ; du point de vue du développeur, cela peut donner l’impression d’être malin en les utilisant, mais tout le reste doit attendre que l’opération se termine

  • Dans les projets open source ou les programmes maintenus sur le long terme, ce genre de chose arrive souvent
    Quand la base de code grossit, on finit par prendre en charge des choses qui n’étaient pas prévues au départ
    Quand les fonctionnalités se multiplient, le nombre d’utilisateurs augmente aussi ; certains n’utilisent que les anciennes fonctions, d’autres adoptent les nouvelles, et au final une certaine valeur devient le défaut de fait au point de ne plus sembler optionnelle
    En prenant Redis comme exemple, si l’on désactive AOF, il fonctionne comme un cache volatile en mémoire, mais la plupart des gens ne le voient même pas ainsi
    D’où l’argument selon lequel moins de fonctionnalités et plus de simplicité valent mieux, et dans ce contexte Memcached est un exemple de cette approche des contraintes bénéfiques
    Pour une grande équipe, cela a tout son sens, mais les projets open source ont besoin de mises à jour régulières pour continuer à obtenir du financement ou des contributions, donc il y a une tension inhérente
    Cela mène parfois à des forks ou projets dérivés spécialisés dans une niche
    Personnellement, je pense qu’il n’y a pas de bonne réponse universelle et que tout dépend du contexte
    Parce que la communication non plus n’est pas gratuite

    • Le fait que la communication ne soit pas gratuite, c’est précisément le problème que j’ai avec les microservices
      Les développeurs ont l’air de l’ignorer complètement
    • L’exemple le plus clair, c’est que les gens pensent que Redis ne sert que de cache qui perd ses données en cas de crash ou d’arrêt
      À mon avis, c’est parce qu’ils ont remplacé Memcached par Redis en s’attendant exactement à la même chose
    • À grande échelle, AOF finit par provoquer des incidents, donc on le désactive
      Cela reste malgré tout un excellent cache
  • J’ai pas mal travaillé avec Flask ces dernières années, pas à plein temps, mais comme élément de la stack technique d’une petite activité e-commerce
    Avec MongoEngine, SQLAlchemy, Celery et les stacks Python pour Google/eBay/Shopify, j’ai rencontré toutes sortes de pièges et d’étrangetés, mais jamais avec Redis
    C’est peut-être parce qu’on ne donne pas les droits d’admin à quiconque considère Redis comme un stockage persistant, mais honnêtement je décrirais Redis comme une technologie extrêmement robuste et bien conçue
    L’API est d’une simplicité extrême, et dès qu’il faut faire quelque chose d’un peu inhabituel, il existe une manière raisonnable et bien pensée de le faire

    • Je suis justement en train de démarrer un projet avec Flask, SQLAlchemy et Celery ; j’aimerais bien en savoir plus sur les raisons d’éviter Celery et sur ce qu’il faudrait utiliser à la place
    • Dans mon univers, les systèmes de cache comme memcached et Redis sont juste des caches où l’on met des données et où on les récupère
      J’imagine qu’on peut utiliser des systèmes d’invalidation comme le tagging
      Je me demande sincèrement quelles sont les choses « bizarres » qu’on peut faire avec un système de cache, et ce que les gens en font au-delà du simple cache de données
  • J’aime memcached, mais si vous configurez Redis comme cache volatile et que les gens le traitent comme un stockage de données persistant, ce n’est pas la faute de Redis
    La comparaison est d’autant plus étrange que memcached non plus n’est pas persistant

    • Dans beaucoup d’entreprises, probablement la plupart, Redis n’est pas perçu ni exploité comme un cache pouvant disparaître à tout moment, mais comme une véritable base de données de production durable
      Sans indication contraire, on ne peut pas vraiment reprocher à un nouveau développeur de partir de cette hypothèse
  • Memcached a été le sauveur du caching à son époque
    Le fait qu’il ait été créé en 2003 par Brad Fitzpatrick pour LiveJournal est aussi appréciable
    Chaque publication dans le fil d’un utilisateur pouvait avoir ses propres restrictions d’accès, ce qui permettait de mettre en cache les publications ou des pages entières
    Je l’ai utilisé pendant des années avec Ruby on Rails, les pages étaient plus rapides et ça fonctionnait, tout simplement
    Son inconvénient — et son avantage en termes de vitesse — était que le cache était stocké en mémoire et non sur disque
    Si vous devez mettre en cache un grand volume de données sur un site à grande échelle, les coûts d’hébergement peuvent devenir élevés
    Dans ce genre de cas, Solid Cache a été un sauveur pour moi
    Sur le projet sur lequel je travaille actuellement, le cache dépasse les 100 GB, il est stocké sur disque dans PostgreSQL, consulté rapidement via des index, et Rails gère automatiquement l’expiration en supprimant les lignes concernées
    Si la taille du cache était plus petite et que j’utilisais déjà Redis, je choisirais probablement simplement Redis
    Mais si la vitesse était la priorité absolue, je ferais un benchmark entre Memcached et Redis

  • Le caractère temporaire de memcached et le fait que les gens l’utilisent ou non comme si c’était persistant sont deux sujets différents
    Si le taux de hit du cache semble être de 99,9 % et qu’il est toujours là, tôt ou tard quelqu’un écrira du code qui dépend de ce comportement
    Je me dis qu’en mode développement, une bibliothèque cliente pourrait peut-être aider en renvoyant null dans environ 10 % des cas

  • memcached est absurdement plus rapide que Redis pour un simple cache clé-valeur
    Il a des threads et il est hautement optimisé pour faire une seule chose extrêmement bien
    Redis, à l’inverse, ressemble davantage à une sorte de tas Python partagé arbitraire avec toutes ses structures de données et son fonctionnement en thread unique
    Chez Notion, Redis est utilisé pour plusieurs usages, mais le vrai caching est confié à memcached

    • On peut constater que pour le clé-valeur, l’écart n’est pas si énorme
      On est autour de 300 microsecondes contre 350 microsecondes en moyenne par lecture
      Le fait qu’il soit mono-thread n’a pas non plus tant d’importance, car le goulot d’étranglement n’est pas le CPU mais les E/S réactives
    • Les threads ne sont pas gratuits
      Ils permettent d’utiliser davantage de cœurs CPU, mais si la charge n’est pas si élevée, un memcached mono-thread consomme moins de CPU qu’une version multithread