Cache em Java: Redis, Memcached e soluções integradas: do A ao Z
Cache é uma das ferramentas-chave para melhorar o desempenho de aplicações Java. Ele permite reduzir o tempo de resposta, diminuir a carga em bancos de dados e APIs externas, além de melhorar a experiência do usuário. Neste artigo, abordaremos três principais abordagens para cache em Java: soluções integradas (ConcurrentHashMap, Caffeine, Ehcache), sistemas distribuídos (Redis) e caches de rede leves (Memcached). Você aprenderá quando e o que escolher, e obterá exemplos de código prontos.
O que é cache e por que ele é necessário em Java?
Cache é o armazenamento temporário de dados frequentemente solicitados em um armazenamento rápido (geralmente na memória RAM). Em Java, isso é especialmente relevante para:
- Redução de latência — os dados do cache são retornados em microssegundos, não em milissegundos (como do banco de dados).
- Diminuição da carga em recursos — bancos de dados, serviços externos e discos trabalham menos.
- Economia de recursos — menos operações de CPU e I/O.
As principais métricas de cache: hit ratio (percentual de acessos bem-sucedidos) e TTL (tempo de vida da entrada).
Soluções integradas: do simples ao avançado
Para projetos pequenos ou cenários single-thread, podem ser usadas estruturas de dados simples. No entanto, para aplicações de produção, bibliotecas especializadas são mais adequadas.
1. ConcurrentHashMap — cache básico
A maneira mais simples é usar ConcurrentHashMap. Ele é thread-safe, mas não possui mecanismos de limpeza automática (TTL, eviction).
import java.util.concurrent.ConcurrentHashMap;
public class SimpleCache { private final ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();
public void put(String key, Object value) { cache.put(key, value); }
public Object get(String key) { return cache.get(key); }
public void evict(String key) { cache.remove(key); }
public void clear() { cache.clear(); }}Vantagens: simplicidade, zero dependências.
Desvantagens: sem invalidação automática, gerenciamento manual de memória.
2. Caffeine — cache moderno de alto desempenho
A biblioteca Caffeine é o padrão de facto para cache in-memory em Java. Ela suporta:
- Expulsão automática por tempo (TTL) e tamanho.
- Carregamento assíncrono.
- Estatísticas (hit rate, miss rate).
import com.github.benmanes.caffeine.cache.Cache;import com.github.benmanes.caffeine.cache.Caffeine;import java.util.concurrent.TimeUnit;
public class CaffeineExample { public static void main(String[] args) { Cache<String, String> cache = Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) .maximumSize(1000) .recordStats() // para coletar estatísticas .build();
// Adicionar cache.put("user:1", "Alice");
// Obter com carregamento automático String value = cache.get("user:2", key -> loadFromDB(key)); System.out.println(value); }
private static String loadFromDB(String key) { // Simulação de carregamento do banco de dados return "Bob"; }}Vantagens: alto desempenho, flexibilidade, tamanho pequeno (cerca de 600 KB).
Desvantagens: apenas local (não distribuído).
3. Ehcache — cache in-process poderoso
Ehcache é uma biblioteca madura (parte do Hibernate). Suporta:
- Armazenamento em disco (persistência).
- Clusterização (via Terracotta).