Estratégia de Caching em .NET — Comparação entre Cache Compartilhado e Cache Privado — Tiago Tartari

Tiago Tartari
6 min readNov 6, 2020

Em aplicações onde a proposta é trazer um grande fluxo de acessos simultâneos, uma boa estratégia de caching significa avançar na etapa de criar uma aplicação de alta disponibilidade.

Nesse post trago uma comparação entre o uso do Redis como cache compartilhado e o uso de Memory Cache em .NET como estratégia de cache privado.

Nesse sentido, a principal diferença entre utilizar um cache distribuído e utilizar um cache privado é o quanto sua aplicação vai escalar.

Nota: Considera-se cache privado aquele utilizado pela própria instância de aplicação, no caso de aplicações em .NET no mesmo servidor utilizando Memory Cache

No desenho abaixo, temos um exemplo clássico de utilização de cache privado.

A cada requisição verifica-se se o objeto está em memória, caso não esteja, uma requisição é feita no banco de dados, entretanto, se o objeto está em memória o retorno é praticamente instantâneo, cenário esse esperado.

O maior risco em adotar a estratégia de cache em memória é a escala. Veja a imagem abaixo.

A medida que mais servidores são adicionados, mais chamadas ao banco de dados ocorrerão, uma vez que os caches não são sincronizados.

Quando falo que o cache não é sincronizado, estou falando que cada servidor terá um tempo diferente de expiração das chaves, causando, inclusive, inconsistências dos dados armazenados.

Vantagens e Desvantagens

Nota: Os prós e contras aqui relacionados são com base às cicatrizes que adquiri ao longo da minha experiência em aplicações com grande volume de acesso. Não há problema algum se você discordar ou identificar mais itens.

Cache Privado

Vantagens

  • Performance é o maior beneficio da utilização do cache privado, isso porque essa abordagem não trás a latência de rede a cada hit, além de não ter a necessidade de desserialização do objeto, uma vez que o próprio objeto está em memória;
  • Por fazer parte do framework a implementação é muito simples e não exige a infraestrutura de rede ou servidores extras;
  • Efetivo para armazenar pequenas quantidades de chaves em cache.

Desvantagens

  • Há uma restrição para a quantidade de memória do servidor. Como o principal recurso dessa abordagem é a memória em caso de falta de memória a aplicação poderá ficar indisponível;
  • Maior custo para aquisição de servidores, uma vez que memória pode tornar gargalo;
  • No caso de muitos servidores/nós:
  • A consistência das informações em cache se tornará um grande problema, uma vez que, na prática temos momentos diferentes de expiração do cache;
  • Maior pressão no banco de dados a medida que as chaves de cache expiram;
  • Se por restrições financeiras for necessário adicionar mais instâncias de aplicações no mesmo servidor com chaves de cache semelhantes, um novo espaço em memória é criado ou seja, a mesma informação não é compartilhada entre instâncias. A imagem abaixo reflete este cenário.
  • Dificuldade de monitoramento das chaves de cache;

Cache Distribuído

Vantagens

  • Eliminamos a possibilidade de dados em cache estarem divergentes entre os servidores/nós;
  • Diversas aplicações podem acessar o mesmo dado do cache, ou seja, não temos cache exclusivo por aplicação;
  • Menor pressão dos servidores/nós de aplicação, eventualmente, reduzindo custos de aquisição de memória;
  • Removemos os riscos de indisponibilidade de falta de memória nos servidores/nós de aplicação;
  • Remoção SPOF, single point of failure, uma vez que o cache é altamente escalável;
  • Monitoramento e Troubleshooting mais aperfeiçoado das chaves de cache.

Desvantagens

  • Menor performance comparado ao cache privado, pois teremos incidência da latência de rede;
  • Aumento do custo em infraestrutura, tanto on-premise quanto cloud;

Enfim, minha percepção é que ao ir para uma estratégia de cache distribuído, temos mais vantagens do que desvantagens, embora eventualmente o custo pode ser superior.

Lembrando, que ao maximizar a disponibilidade implicaremos no aumento dos investimentos em TI.

Na prática

Especificamente nesse post vou abordar a utilização do Redis, entretanto, outros caches distribuídos podem ser considerados. Para aquelas que pensam em cloud a Microsoft disponibiliza Redis como PaaS.

1º Abordagem: Criando as chaves de cache

Notamos diferenças significativas comparando a execução do cache privado com o cache distribuído.

Imagen 1: Utilizando cache distribuído

Imagen 2: Utilizando cache privado

Quantidade de Requisições por Segundo

Nos primeiros 30 segundos, com cache privado chegamos a um pouco mais 500 requests por segundo, utilizando o cache distribuído o resultado foi um pouco mais que 300 requests por segundo.

A diferença entre as duas versões é a serialização do objeto para guardar no Redis, nesse caso consigo enxergar oportunidades futuras de melhorias, para fazer a serialização utilizei o System.Text.Json mas podemos comparar, por exemplo, com o Jil ou o Newtonsoft e vermos qual tem melhor performance.

Alocação de Memória e Pressão do Garbage Collector

Também é possível notar diferença significativa na quantidade de memória alocada. Utilizando o cache privado, como é de se esperar, a quantidade de memória alocada é 10x mais que a alocação de memória ao utilizar cache privado.

O GC trabalha mais em linha na opção de cache distribuído, o que faz total sentido quando comparado ao cache privado onde o estado do objeto precisa ser mantido em memória.

2º Abordagem: Consumindo os dados em cache

Notamos diferenças significativas comparando a execução do cache privado com o cache distribuído.

Imagen 1: Utilizando cache distribuído

Imagen 2: Utilizando cache privado

Quantidade de Requisições por Segundo

A quantidade de requisições por segundo ficaram semelhantes, ou seja, mesmo necessitando da desserialização não foi percebido impactos na quantidade de requisições. Um ótimo resultado!

Alocação de Memória e Pressão do Garbage Collector

O resultado é assustador, mais uma vez para o cache distribuído conseguimos manter 10x menos a alocação de memória no cache privado.

Notamos que no cache privado a memória não se recompõe, o que é um padrão aceitável, entretanto, no cache distribuído após o teste de carga, notamos que o consumo cai pela metade.

Conclusão

Há momentos que precisamos utilizar cache privado, inclusive entendemos os motivadores para essa decisão, também entendemos as restrições dessa abordagem.

Entretanto, a deficiência de memória na abordagem de cache privado é suprida pela abordagem de cache distribuído. Do mesmo modo, a escala que é possível com cache distribuído não se compara aos riscos de indisponibilidade do cache privado.

Originally published at https://www.tiagotartari.net on November 6, 2020.

--

--

Tiago Tartari

Microsoft MVP, programador por mais de 18 anos onde 10 deles atuando como arquiteto de soluções para e-commerce, palestrante técnico, apaixonado por performance