Nessa série de posts estamos furando a bolha, saindo do desenho básico de 3 camadas, indo além. Hora vamos abordar práticas, hora vamos abordar ferramentas.
Hoje vamos falar de uma ferramenta, o Redis.
Ele pode ser seu aliado na hora de hospedar dados pré-processados.
Disclaimer sobre a persistência do Redis
Leia a página Redis Persistence.
- Nunca armazene algo no redis que não possa ser perdido.
- Se o dado só existe no redis, está errado na maioria das vezes.
- Se o redis cair, ou restartar, perdas de dados são aceitáveis. Se estiver com persistência ligada ainda pode perder o último segundo antes da queda. Isso é esperado e normal.
- Tente, na medida do possível usar um fluxo no código que tente obter do redis, caso não exista, cria-se um lock, produz o dado da forma lenta, depois preencha o cache, libere o lock e entregue a resposta para o requisitante. Esse fluxo é muito eficiente.
- A natureza de sua persistência faz com que não seja um repositório resiliente e por isso a utilização como message broker deve levar em consideração esse aspecto. RabbitMQ é sempre a opção mais aconselhada para esse papel.
REDIS
“Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache, and message broker.”
Redis é um Data Store em memória, usado como database, cache e message broker e está sob a licença BSD.
Essa é a tradução ao pé da letra.
Redis é um Data Store em memória, usado normalmente como cache e lock distribuído (message broker, não).
Redis será nosso aliado em diversos momentos. Ele não só nos suporta para promover uma redução do acesso a banco, favorecendo a redução do load de banco, mas também suportará aplicações distribuídas, servindo de centralizador para dados cacheados. Isso é importantíssimo quando falamos de múltiplas instâncias de serviços que não compartilham memória.
Embora o redis possua estruturas complexas de dados, na maior parte do tempo usaremos uma key (string) e um dado (json ou binário).
Keys
Parte da mágica no uso do Redis está na forma como as keys são criadas, em geral interpolando dados de negócio com constantes para produzir chaves únicas e fáceis de serem geradas novamente.
Vamos supor que eu seja o usuário de ID 7, de login “luizcarlosfaria” e tenha logado em sua aplicação. De posse do ID do usuário (que vem do token) você poderia criar uma chave “sessao:usuario:7” ou no caso do login “sessao:usuario:luizcarlosfaria” .
Pronto, com base no token você consegue obter o ID ou login para conseguir recriar a chave, que será a Key para você armazenar e recuperar minha sessão a partir do redis. Toda vez que você precisar a minha sessão, você geraria essa key e tentaria buscar esse dado lá no redis. Caso não exista nada lá, significa que nunca foi gerado ou que o tempo de vida daquele dado expirou.
Aí você pode se perguntar: Mas pelo fato do dado ficar em memória, isso não acabaria com a memória do servidor?
TTL
No Redis você você tem a opção de definir um TTL (time to live) para toda key, um tempo de vida. A leitura, escrita e definição de TTL são operações individuais e você está no controle.
Normalmente juntamos escrita com definição de TTL e abstraímos em uma operação só.
Embora seja possível, é incomum ver operações como keep alive, que consiste em resetar o TTL com o valor default dando sobrevida ao cache a cada leitura. Não é uma má prática, só não encontro com frequencia.
Cache
Se esse objeto sessão for composto pela resposta de 5 queries, por exemplo, estamos trocando 5 queries mais o processamento de conversão desse objeto (do ADO para algumas classes) para 1 acesso ao redis. Estamos reduzindo 5 idas ao banco, ou aos bancos, talvez algumas chamadas de API e trocamos isso tudo por uma operação super simples de ida ao cache.
Essa é mais uma das estratégias que visam reduzir o consumo de banco de dados.
Agora imagine que os dados de um produto, em um e-commerce (nome, descrição etc, paths das imagens) esteja no cache. Vamos supor que a página desse produto receba 300 requisições por segundo, durante 15 segundos.
Um simples cache de 3 segundos evitaria 4495 acessos a banco, enquanto um cache de 5 segundos evitaria 4497 acessos nesse período de 15 segundos. Claro, supondo que para obter esses dados seja necessário realizar apenas 1 query por pageview (sem cache).
Essa é inclusive a primeira falácia que precisamos descontruir:
Quanto maior o volume de acesso a um objeto, menor pode ser seu tempo de cache que ainda assim valerá a pena.
Dois fatores são importantes aqui:
- Criação do objeto é muito custosa. Quando mais custosa, mais se justifica a utilização de cache.
- O acesso a esse objeto é intenso.
Volatilidade também é um assunto pertinente, mas é também um assunto mais avançado. Na prática ignoramos a volatilidade até que esta seja um problema. Então tratamos o problema em vez de pensar em deixar de usar o cache.
Lock
Outra oportunidade que temos com REDIS é o Lock Distribuído.
Lock Distribuído é uma forma de semáforo onde diversos componentes distribuídos concorrem para lockar um objeto. Uma corrida ocorre e somente um é o vencedor. O vencedor executa sua operação e libera o lock. Os demais participantes da corrida, tentam novamente realizar o lock, no final, todos executarão, mas nunca executarão simultaneamente para o mesmo objeto.
Com Redis, o Lock Distribuído é feito com base na chave. Segue o mesmo princípio “lock:agenda:usuario:10”, nessa chave estamos lockando a agenda do usuário 10. A thread ou instância que ganhar o lock, poderá escrever no SQL Server, DB2, Oracle ou qualquer banco ou API. É indiferente.
O Redis só está lá para garantir o lock, na prática o objeto de banco não está de fato lockado. O seu banco de dados nem sabe que há um REDIS na arquitetura. Mas sua aplicação respeita o lock porque se você tem uma demanda de não permitir concorrência para escrita em algum objeto, é assim que implementamos.
Talvez o exemplo de transação bancária faça mais sentido.
Se temos conta origem 11, e conta destino 15, devemos criar um lock ou nas duas contas, ou somente na conta de origem (a conta em que retiraremos dinheiro).
Conclusão
Redis é uma mão na roda para performance e quando falamos de processamento paralelo, ele também pode nos ajudar.
Redis é minha primeira opção na hora que o projeto cresce.
Soluções como o NHibernate conseguem se integrar com o Redis o que possibilita coisas incríveis. Mas se você não está usando NH, não tem problema, você pode fazer cache de qualquer objeto.
Kong, como API Gateway tem a capacidade de usar o Redis como storage para cache em sua versão paga. Isso faz com que todas as instâncias de um cluster seja capaz de ter seus dados sincronizados, evitando inconsistência natural produzida com cache local, em memória em cluster.
Para Lock existem libraries dedicadas e você pode conferir uma lista aqui.
SPOILER: Quando a gente falar de mensageria, vamos falar mais de lock em computação distribuída.
0 comentários