1 pontos por GN⁺ 4 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • O cache é introduzido para reduzir a carga do banco de dados, mas ferramentas fáceis de usar como o Redis tendem, com o tempo, a se tornar algo do qual se depende como se fosse um armazenamento persistente
  • O problema não é a funcionalidade de persistência do Redis em si, mas o fluxo operacional em que um componente introduzido inicialmente como cache volátil acaba se envolvendo com o estado central da aplicação
  • O memcached, desde sua definição oficial, é um sistema distribuído de cache de objetos em memória e não pressupõe armazenamento em disco, o que facilita tratá-lo como uma carga de trabalho de cache sem estado
  • Várias instâncias de memcached não são divididas por um servidor, mas pelo cliente, usando uma lista de URLs e hash de chaves; nós com falha saem do hasher e depois tentam se reconectar
  • Em vez de adicionar cache primeiro porque “o banco de dados está lento”, é preciso verificar antes consultas lentas e índices ausentes

O momento em que o Redis deixa de ser cache e vira armazenamento

  • Ao operar infraestrutura, o pedido “precisamos de cache” aparece com frequência, e é fácil pensar primeiro no Redis, por ser familiar e cheio de recursos
  • A página inicial do Redis destaca o Redis Iris, um mecanismo de contexto em tempo real para apps de IA, mas é uma direção compreensível para uma empresa que precisa gerar receita
  • Depois de implantar o Redis e passar a string de conexão, no início ele funciona como um cache confiável

Problemas que surgem alguns meses depois

  • Com o tempo, como cache.set("key", "value") é muito mais simples que INSERT INTO table VALUES ('key', 'value'), as pessoas começam a tratar o Redis assim:
    • Um componente que está sempre presente, um lugar para preservar dados. Um banco de dados de fato
    • Passa-se a enxergar o REmote DIctionary Server não como cache volátil, mas como armazenamento permanente
  • Nem você nem seus colegas da equipe de operações percebem isso, e como presumem que o cache será tratado como volátil, o sistema de alertas também não detecta isso
    • O problema só aparece quando algo é feito no Redis, como upgrade, migração de nó ou um acidente em que a bandeja de HDD de um servidor RAID0 é removida
  • O problema central não é que o Redis não tenha persistência, mas o descompasso na suposição de que as pessoas não tratam como cache o Redis que foi introduzido como cache
  • Quando essa dependência é descoberta tarde demais, o Redis já está profundamente entrelaçado com a aplicação e fica difícil removê-lo; no fim, ele precisa ser mantido e monitorado como um “pet”

Por que o memcached é mais direto no papel de cache

  • O memcached é “um sistema gratuito, open source, de alto desempenho e distribuído de cache de objetos em memória”, um cache de uso geral para reduzir a carga do banco de dados e acelerar aplicações web dinâmicas
  • Em frameworks que oferecem cache plugável, como o Django, é possível trocar o backend de cache
  • Mesmo com menos recursos que o Redis, há motivos para escolher o memcached porque suas características operacionais são mais simples
    • É fácil lidar com downtime: bibliotecas cliente muitas vezes ignoram exceções de conexão, e um simples get pode retornar valor padrão ou None mesmo se o servidor estiver fora do ar
    • O memcached não tem clustering embutido, o que na prática torna o clustering mais conveniente
      • Ao configurar várias URLs na biblioteca cliente, a instância de destino é escolhida pelo hash da chave
      • Se a chamada do cliente detectar que uma instância caiu, remove o nó do hasher e tenta se reconectar automaticamente após algum tempo
    • A carga da persistência é estruturalmente menor: como o memcached não grava em disco, é fácil agendá-lo em qualquer lugar como workload sem estado
  • Dá para montar um modo operacional parecido com Redis, mas a arquitetura do memcached está mais próxima dessa direção, então é mais intuitivo tratá-lo como cache
  • O memcached é uma aplicação relativamente simples, e o fato de ser possível executar dezenas de instâncias com cerca de 64 MB de cache com quase nenhum overhead é um motivo para escolhê-lo
  • Muitos problemas de “o banco de dados está lento” na verdade começam com consultas lentas ou índices ausentes, então além de adicionar cache, também é preciso olhar para a otimização das consultas
  • Se você tiver curiosidade sobre as decisões de design do memcached, há muitos textos interessantes no blog do memcached; um deles é o publicado em maio, “Quanto tempo essa resposta realmente leva? (How Long Does That Response Take… For Real?)”

1 comentários

 
GN⁺ 4 시간 전
Opiniões do Hacker News
  • Redis é uma ótima tecnologia, mas me parece que ele sofre por tentar cumprir bem, ao mesmo tempo, dois papéis diferentes: estrutura de dados persistente e cache volátil
    Na prática, até no próprio Redis os dois não se misturam tão bem, já que a persistência é algo ativado ou desativado globalmente
    Para cache puro, eu usaria memcached ou algo equivalente, e só usaria Redis com persistência ativada quando precisasse de estruturas de dados como placares
    No $WORK, não adotamos nenhum dos dois, e a camada de cache para trabalhos lentos mantém dados tanto no sistema de arquivos quanto em uma tabela de banco usada como armazenamento de chave-valor
    O banco ajuda a coordenar o thundering herd, leituras no mesmo servidor atingem só o sistema de arquivos, e leituras de outros servidores consultam o banco uma vez antes de manter no sistema de arquivos
    Poderíamos trocar a camada de sistema de arquivos por memcached, mas até agora isso tem funcionado muito bem

    • Cheguei praticamente à mesma conclusão depois de lidar com o Memcachedb (memcache + bdb para persistência) no fim dos anos 2000
      O Redis claramente tinha muito mais recursos, e o antirez também era uma figura carismática e surpreendentemente humilde, então dá para entender por que o Redis ficou mais popular
      Ainda assim, para mim o memcached sempre foi o ápice de escolha tecnologia chata
      Como engenheiro de plataforma, consigo dar suporte aos dois, mas quando desenvolvedores começam a usar recursos avançados do Redis como persistência, replicação e clustering, eu tento confirmar se eles realmente entendem as desvantagens dessa decisão
    • Dá para fazer muita coisa usando uma tabela de banco como armazenamento de chave-valor junto com o sistema de arquivos, antes de pagar o custo de configurar um armazenamento de cache dedicado
      Sempre que proponho esse tipo de solução, acabo brigando inúmeras vezes no ambiente de engenharia com gente inexperiente que sente que cache obrigatoriamente precisa estar em um armazenamento dedicado
  • Isso não quer dizer, de forma alguma, que o memcache evite esse tipo de problema
    Em meados dos anos 2000, trabalhei com um sistema escalado que usava memcache, e os desenvolvedores caíram exatamente nas mesmas armadilhas citadas no texto sobre Redis
    Tentaram contornar as leis dos sistemas distribuídos com memcache e, por causa do vício em cache, dimensionaram o conjunto de servidores assumindo que o memcache estaria ligado; quando havia falha, tudo explodia de repente como um DDoS
    Também havia amplificação de escrita quando algum host expulsava uma chave com TPS alto e todos os outros hosts tentavam repovoá-la batendo no serviço do qual dependiam; chaves quentes criavam hosts quentes, e subir o memcached junto com o daemon de serviço levava a picos misteriosos de CPU
    Chamadas de memcache também acabavam indo para um buraco negro por causa da persistência de entradas antigas de DNS
    Tudo isso poderia ter sido evitado com um uso melhor do memcache, mas a tentação de abusar era forte demais

  • Acho que já vi em produção quase todos os problemas de Redis/Valkey mencionados pelo autor
    Já houve incidente em que o Valkey, sem política de memória, consumiu toda a memória e gerou erro de escrita no append-only file, e também já vi falha de escrita no AOF porque o disco realmente lotou
    Também já houve erro 500 porque se assumia completamente que o Redis estaria vivo, rodando e preenchido com todos os dados de usuário, sem nenhum mecanismo para voltar ao caminho lento
    Também já vi uso criativo de sorted sets e outras estruturas de dados, dependendo do fato de que esses conjuntos jamais seriam expulsos
    Apesar dessas observações em campo, ainda acho difícil recomendar memcache antes de Redis
    Pode ser complicado projetar a aplicação para ter uma disposição de cache amigável ao memcache, e se uma equipe grande o bastante usar memcache, é muito provável que ela acabe encontrando um caminho que exija Redis
    Aí você passa a manter 2 tecnologias de cache

    • Se alguém decide usar Redis para algo que não seja cache, então na prática você já tem 2 tecnologias de cache
      Uma instância de Redis configurada para cache não pode ser usada para outros fins; a instância de cache precisa ter expulsão, e a instância não-cache não deve ter expulsão
      No fim, você precisa de um segundo Redis com configuração diferente
      Sinceramente, projetar o app para uma disposição de cache amigável ao memcache é o mesmo que projetá-lo para uma disposição de cache amigável ao Redis
      Os padrões desse tipo de cache de aplicação são os mesmos: buscar e, se não houver, calcular e gravar
    • Se ainda não existir, eu criaria uma interface de abstração para pedir a chave, passando uma função assíncrona ou lambda para buscar o valor na origem em caso de cache miss
      var value = cache.lookup( keyname, () => db.query(...), TimeSpan.FromMinutes(5) // or CacheOptions );
      Assim, em caso de cache miss, você pode ir direto para o caminho alternativo ou fazer a inserção
    • Não manter 2 tecnologias de cache é um argumento que sempre vence
  • Outra característica pouco mencionada do memcache é que todas as operações são O(1) por design
    Foi uma escolha consciente dos autores, então há limitações, mas isso garante que não haja travamentos aleatórios em operações simples
    Já o Redis, por ter um núcleo de design single-thread, pode executar operações de complexidade arbitrária, e do ponto de vista do desenvolvedor isso pode dar a sensação de estar sendo esperto ao usá-las, mas todo o resto fica esperando até essa operação terminar

  • Em projetos open source ou programas mantidos por muito tempo, isso acontece com frequência
    Quando a base de código cresce, ela acaba começando a suportar coisas que não estavam no plano original
    Quando os recursos aumentam, os usuários também aumentam; algumas pessoas usam só os recursos antigos, outras adotam os novos, e no fim algum valor específico vira o padrão de facto e deixa de parecer realmente opcional
    Usando o Redis como exemplo: se você desativar o AOF, ele funciona como um cache volátil em memória, mas a maioria das pessoas nem pensa nele dessa forma
    Daí surge a lógica de que ter menos recursos e ser mais simples é melhor, e, nesse contexto, o Memcached é um exemplo dessa abordagem de camisa de força
    Para equipes grandes isso faz total sentido, mas projetos open source precisam de atualizações regulares para continuar recebendo financiamento ou contribuições, então existe uma tensão inerente aí
    Às vezes isso leva a forks ou projetos derivados especializados em um nicho específico
    Pessoalmente, acho que não existe resposta certa; depende do contexto
    Porque comunicação também não sai de graça

    • O fato de comunicação não ser de graça é o problema que eu tenho com microsserviços
      Os desenvolvedores parecem agir como se isso simplesmente não existisse
    • O exemplo mais claro é que as pessoas acham que o Redis funciona apenas como um cache que perde dados em caso de crash ou encerramento
      Acho que isso acontece porque trocaram o Memcached por Redis esperando exatamente o mesmo comportamento
    • Em grande escala, o AOF acaba causando falhas, então as pessoas o desligam
      Ainda assim, continua sendo um cache excelente
  • Trabalhei bastante com Flask nos últimos anos e, embora não em tempo integral, usei isso como parte da stack de uma pequena empresa de e-commerce
    Já passei por todo tipo de armadilha e esquisitice com MongoEngine, SQLAlchemy, Celery e stacks Python para Google/eBay/Shopify, mas nunca com Redis
    Talvez seja porque eu não dê privilégios administrativos a ninguém que ache que Redis é um armazenamento persistente, mas, sinceramente, eu descreveria o Redis como uma tecnologia extremamente robusta e bem projetada
    A API é extremamente simples e, sempre que você precisa fazer algo meio estranho, existe um jeito razoável e bem pensado de fazer

    • Estou começando um projeto agora com Flask, SQLAlchemy e Celery, então gostaria muito de ouvir mais sobre por que evitar o Celery e o que usar no lugar
    • No meu mundo, sistemas de cache como Memcached e Redis são só caches de guardar e buscar
      Talvez eu até usasse um sistema de invalidação como tagging
      Tenho curiosidade sincera sobre que tipo de coisa estranha dá para fazer com um sistema de cache, além de simplesmente guardar dados em cache; o que as pessoas fazem com caches?
  • Eu gosto do memcached, mas, se você configurar o Redis como cache volátil e as pessoas passarem a tratá-lo como um armazenamento persistente de dados, isso não é culpa do Redis
    A comparação é especialmente estranha porque o memcached também não é persistente

    • Em muitas empresas, provavelmente na maioria, o Redis não é visto nem operado como um cache que pode sumir a qualquer momento, e sim como um banco de dados de produção durável de verdade
      Se ninguém disser o contrário, não é absurdo que um desenvolvedor novo assuma isso
  • O Memcached foi o salvador do caching na época em que foi lançado
    Também é legal que ele tenha sido criado em 2003 por Brad Fitzpatrick para o LiveJournal
    Cada post no feed do usuário podia ter restrições de acesso diferentes, e isso permitia fazer cache de posts ou até de páginas inteiras
    Usei por vários anos junto com Ruby on Rails, as páginas ficaram mais rápidas e simplesmente funcionava
    A desvantagem — e também a vantagem em termos de velocidade — era que o cache ficava armazenado em memória, e não em disco
    Se você tem um site grande com um volume enorme de dados para cachear, o custo de hospedagem pode ficar alto
    Nesses casos, o Solid Cache foi um salvador para mim
    No projeto em que estou trabalhando agora, o cache tem mais de 100 GB, fica armazenado em disco no PostgreSQL, é consultado rapidamente por índices, e o Rails cuida automaticamente da expiração removendo as linhas correspondentes
    Se o cache for menor e você já estiver usando Redis, provavelmente eu simplesmente usaria Redis
    Mas, se velocidade for a prioridade número 1, eu testaria Memcached e Redis em benchmark

  • O fato de o memcached ser efêmero e a questão de as pessoas o usarem ou não como se fosse persistente são coisas separadas
    Se a taxa de acerto do cache parece ser de 99,9% e ele está sempre lá, mais cedo ou mais tarde alguém vai escrever código dependente desse comportamento
    Talvez, em modo de desenvolvimento, a biblioteca cliente pudesse ajudar retornando null em uns 10% dos casos

  • o memcached é absurdamente mais rápido que o Redis para o trabalho simples de cache chave-valor
    Ele tem threads e é altamente otimizado para fazer uma coisa só, e fazer isso muito bem
    Já o Redis parece mais uma espécie de heap Python compartilhado arbitrário, com todas as estruturas de dados e thread única etc.
    Na Notion, eles usam Redis para várias finalidades, mas deixam o caching de fato com o memcached

    • Dá para confirmar que, em chave-valor, a diferença não é tão grande assim
      Fica em algo como 300 microssegundos contra 350 microssegundos por leitura em média
      E o fato de ser thread única também não importa tanto, porque o gargalo não é CPU, e sim I/O responsivo
    • Threads não saem de graça
      Elas permitem usar mais núcleos de CPU, mas, se a carga não for tão alta, um memcached de thread única usa menos CPU do que um multithread