Skip to content

Cache for Kotlin backend

Estratégias Abrangentes de Cache para Serviços Backend em Kotlin

Resumo Executivo: Para um backend Kotlin processando 100.000 requisições diárias, o Redis emerge como a solução de cache ideal, entregando latência sub-milissegundo e capacidade 100x maior por apenas R$ 325/mês. O MongoDB, embora atualmente funcional, introduz overhead de latência 2-3x maior e carece de funcionalidades específicas para cache. A abordagem recomendada combina migração imediata para Redis para dados quentes com retenção seletiva do MongoDB para cenários de consultas complexas, entregando ROI de 67-167% no primeiro ano através de performance melhorada e custos de infraestrutura reduzidos.

Esta análise revela que a implementação adequada de cache pode alcançar melhoria de performance 3-5x e redução de carga no banco de dados de 60-80%, posicionando o sistema para crescimento futuro significativo. O padrão de tráfego concentrado (8h-22h) alinha perfeitamente com as características de escalabilidade do Redis, permitindo tratamento contínuo de crescimento de tráfego 10-100x sem mudanças arquiteturais.

Análise do estado atual: MongoDB como cache

O MongoDB atualmente serve como sua camada de cache, mas a pesquisa revela limitações significativas de performance para cargas de trabalho puras de cache. Embora o MongoDB 8.0 entregue operações de leitura 36% mais rápidas que versões anteriores, ele fundamentalmente opera como um banco de dados de documentos ao invés de um cache construído especificamente para esse propósito.

Características de performance na sua escala:

  • Latência: Tempos de resposta P99 de 5.7-23.3ms, variando significativamente com o tamanho dos dados
  • Throughput: Máximo de 20.000-40.000 operações por segundo
  • Eficiência de memória: Usa ~2.7GB para 2.5M registros (melhor que os ~5.7GB do Redis)
  • Gargalo de escalabilidade: Performance degrada rapidamente quando o dataset excede a RAM disponível

Com 100k requisições diárias (1.16 requisições/segundo em média, ~6 requisições/segundo no pico), o MongoDB facilmente lida com sua carga atual, mas introduz overhead de latência desnecessário. As configurações de write concern padrão para “majority” impactam significativamente a performance para operações de cache onde consistência eventual é suficiente.

Análise de custos para a abordagem atual com MongoDB:

  • MongoDB Atlas Flex tier: R$ 40-150/mês para sua escala
  • Penalidade de performance: 2-3x mais lento que caches construídos especificamente
  • Complexidade operacional: Overhead de manutenção maior que serviços gerenciados de cache

A pesquisa identifica o MongoDB como benéfico para cenários específicos: grandes datasets (>10GB), padrões de consulta complexos requerendo agregações, e persistência de dados de longo prazo. No entanto, para cache de respostas de API e resultados computados, soluções construídas especificamente entregam performance superior.

Análise abrangente do Redis

O Redis se destaca como a escolha excepcional para seu backend Kotlin, oferecendo características de performance incomparáveis e capacidades de escalabilidade contínuas. A análise revela que o Redis pode lidar com 100.000+ operações por segundo com latência sub-milissegundo, tornando sua carga atual de 1.2 requisições/segundo essencialmente negligível.

Benchmarks de performance e escalabilidade

O Redis entrega performance consistentemente superior em todas as métricas:

MétricaPerformance RedisPerformance MongoDBVantagem
Operações/segundo100.000+20.000-40.0003-5x maior
Latência P500.16ms5ms30x mais rápido
Latência P99<2ms15-23ms10x mais rápido
Overhead de memória5.7GB por 2.5M registros2.7GB por 2.5M registrosMongoDB 2x mais eficiente
Capacidade de escalaLinear até 1M+ ops/secDegrada além de 40k ops/secRedis superior

AWS ElastiCache Redis 7.1 fornece capacidades excepcionais:

  • Throughput pico: 500M+ requisições/segundo por cluster
  • Capacidade por nó: 1M+ operações/segundo em instâncias maiores
  • Melhorias de latência: 50% de redução na latência P99 versus versões anteriores
  • I/O aprimorado: 100% de melhoria no throughput em instâncias com 8+ cores

Otimização de estrutura de dados para cache

O Redis oferece estruturas de dados avançadas que melhoram significativamente a eficiência do cache:

Hashes para dados estruturados (recomendado para respostas de API):

// 50% de economia de memória vs strings individuais
jedis.hset("user:123", mapOf(
"name" to "João Silva",
"email" to "joao@exemplo.com",
"lastLogin" to "2025-07-22"
))

Strings para cache simples chave-valor:

// Mais rápido para resultados computados e tokens de sessão
jedis.setex("api:product:456", 3600, jsonResponse)

Otimização de configuração para seu caso de uso:

# Desabilitar persistência para máxima performance de cache
save ""
appendonly no
# Otimização de memória
maxmemory-policy allkeys-lru
hash-max-ziplist-entries 512
hash-max-ziplist-value 64

Opções de arquitetura de implantação

Instância Única (fortemente recomendado para sua escala):

  • Lida com 100-1000x seu tráfego atual
  • Opção mais custo-efetiva (R$ 120-250/mês)
  • Complexidade operacional mínima
  • Caminho fácil de escalabilidade vertical

Redis Cluster (desnecessário até >50.000 RPS sustentados):

  • Aumento mínimo de custo de 3x
  • Latência de rede adicional para roteamento de chaves
  • Requisitos complexos de configuração de cliente
  • Benéfico apenas para datasets massivos (>100GB) ou necessidades extremas de throughput

Comparação de soluções alternativas de cache

Além do Redis e MongoDB, várias soluções de cache merecem consideração, cada uma otimizada para padrões arquiteturais específicos e requisitos de performance.

Soluções em memória para implantações de nó único

Caffeine emerge como o campeão de performance para cenários de cache local:

  • Throughput: Até 40M operações/segundo para leituras concorrentes
  • Latência: Tempos de acesso sub-microssegundo (mesma JVM)
  • Eficiência de memória: Algoritmo Window TinyLFU entrega taxas de hit quase ótimas
  • Integração: Provedor de cache padrão no Spring Boot 2.x+
@Configuration
class CaffeineConfig {
@Bean
fun caffeineCache(): Cache<String, Any> = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(1, TimeUnit.HOURS)
.recordStats()
.build()
}

Ehcache fornece capacidades de armazenamento multi-tier:

  • Camadas on-heap, off-heap e persistência em disco
  • Conformidade JSR-107 para cache padronizado
  • Opções de configuração complexas para requisitos empresariais
  • Custo e complexidade maiores versus Caffeine

Alternativas de cache distribuído

Hazelcast oferece computação distribuída em memória:

  • Escalabilidade horizontal linear com distribuição automática de dados
  • Ganhos de eficiência de memória: 1.4GB vs 2.5GB para Java HashMap equivalente
  • Forte integração Java/Kotlin com Spring Boot
  • Core open source com funcionalidades enterprise requerendo licenciamento pago

Memcached fornece simplicidade operacional:

  • Arquitetura multi-threaded otimizando acesso concorrente
  • Protocolo TCP simples com overhead mínimo de serialização
  • Complexidade de configuração zero
  • Limitado a operações simples chave-valor (valores máx. 1MB)

Apache Ignite entrega capacidades analíticas:

  • Consultas SQL distribuídas com suporte ANSI SQL-99
  • Arquitetura híbrida de persistência memória/disco
  • Funcionalidades de machine learning e processamento de streaming
  • Requisitos complexos de implantação e curva de aprendizado íngreme

Matriz de decisão para seus requisitos

SoluçãoPerformanceSimplicidadeCustoDistribuídoMelhor Cenário
Redis⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐Cache de API em escala
Caffeine⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐Aplicações de nó único
Hazelcast⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐Apps Java distribuídas
Memcached⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐Armazenamento simples de sessão
MongoDB⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐Requisitos de consulta complexos

Padrões de arquitetura e implementação

Estratégia de cache multi-tier (recomendado)

A arquitetura ótima combina múltiplas camadas de cache para maximizar performance enquanto minimiza custos:

Camada 1 (L1): Cache Local Caffeine

  • Dados quentes acessados nos últimos 5 minutos
  • Alocação máxima de 1GB de memória
  • Tempos de acesso sub-microssegundo

Camada 2 (L2): Cache Distribuído Redis

  • Dados mornos acessados na última hora
  • Capacidade de 4GB para resultados computados e respostas de API
  • Latência de rede sub-milissegundo

Camada 3 (L3): Cache de Consulta MongoDB

  • Resultados de agregação complexos e grandes datasets
  • Armazenamento persistente para computações caras
  • Tempos de acesso de 5-15ms aceitáveis para consultas complexas
@Service
class MultiTierCacheService(
private val l1Cache: Cache<String, Any>, // Caffeine
private val l2Cache: RedisTemplate<String, Any>, // Redis
private val l3Cache: MongoRepository // MongoDB
) {
suspend fun getData(key: String): Any? {
// Verificação L1 (mais rápida)
l1Cache.getIfPresent(key)?.let { return it }
// Verificação L2 (distribuída)
l2Cache.opsForValue().get(key)?.let { data ->
l1Cache.put(key, data) // Promover para L1
return data
}
// Verificação L3 ou busca no banco de dados
return l3Cache.findByKey(key)?.also { data ->
l2Cache.opsForValue().set(key, data, Duration.ofHours(1))
l1Cache.put(key, data)
}
}
}

Padrões de invalidação de cache

Invalidação dirigida por eventos garante consistência de dados:

@Component
class CacheInvalidationHandler(
private val cacheManager: CacheManager
) {
@EventListener
suspend fun handleDataUpdated(event: DataUpdatedEvent) {
// Invalidar chaves específicas
cacheManager.getCache("products")?.evict(event.productId)
// Invalidar dados relacionados
cacheManager.getCache("categories")?.evict(event.categoryId)
// Acionar aquecimento de cache para itens populares
if (event.isPopularItem) {
warmCacheAsync(event.productId)
}
}
}

Estratégia de expiração baseada em tempo:

  • Respostas de API: 1-6 horas dependendo da frequência de atualização
  • Resultados computados: 2-24 horas para cálculos caros
  • Sessões de usuário: 30 minutos a 24 horas baseado em requisitos de segurança
  • Conteúdo estático: 24-48 horas para dados que raramente mudam

Arquitetura de monitoramento de performance

Coleta de métricas abrangente possibilita otimização proativa:

@Component
class CacheMetricsCollector(
private val meterRegistry: MeterRegistry
) {
private val hitCounter = Counter.builder("cache.hits").register(meterRegistry)
private val missCounter = Counter.builder("cache.misses").register(meterRegistry)
@Scheduled(fixedRate = 60000)
fun calculateMetrics() {
val hitRatio = hitCounter.count() /
(hitCounter.count() + missCounter.count())
meterRegistry.gauge("cache.hit.ratio", hitRatio)
// Alerta sobre degradação de performance
if (hitRatio < 0.8) {
alertingService.triggerAlert("Taxa de hit de cache baixa: $hitRatio")
}
}
}

Benchmarks de performance e análise de custos

Comparação de custos de provedores cloud para 100k requisições/dia

Seu padrão de tráfego (100k requisições/dia concentradas 8h-22h) se traduz em 1.16 requisições/segundo em média com ~6 requisições/segundo no pico, bem dentro da capacidade de qualquer solução de cache.

ProvedorServiçoConfiguraçãoCusto MensalCusto Anual
AWSElastiCache Rediscache.t3.smallR 75 (transfer/monitoring)R$ 3.900
GoogleCloud Memorystore1GB StandardR 55 (network)R$ 3.000
AzureCache for RedisStandard C1R 50 (transfer)R$ 2.520
Redis CloudEssentials1GB instanceR 25 (extras)R$ 1.200

AWS ElastiCache (recomendado) fornece o melhor equilíbrio performance/custo:

  • Opção serverless começando em R$ 30/mês com faturamento pay-per-use
  • 33% menor custo que engines concorrentes
  • Reserved instances oferecem economia adicional de 20-30%
  • Integração superior com o ecossistema AWS

Análise de escalabilidade de performance

Utilização atual de capacidade: Seus 1.16 RPS representam <0.01% da capacidade do Redis, fornecendo margem massiva para crescimento.

Projeções de escalabilidade:

  • Crescimento 10x (1M requisições/dia): Ainda dentro da capacidade de instância única Redis
  • Crescimento 100x (10M requisições/dia): Pode requerer cluster Redis ou instância maior
  • Crescimento 1000x (100M requisições/dia): Requer arquitetura distribuída

Comportamento de escalabilidade de custos:

  • Escalabilidade linear até 10x o tráfego atual na mesma infraestrutura
  • Benefícios exponenciais de custo através de carga reduzida no banco de dados
  • Ponto de equilíbrio ocorre dentro de 6-18 meses baseado em melhorias de performance

Cálculo de ROI e impacto no negócio

Investimento de implementação:

  • Serviço cloud: R$ 2.500-4.000 anualmente
  • Migração e setup: R$ 25.000-75.000 uma vez
  • Ferramentas de monitoramento: R$ 2.500-6.000 anualmente
  • Treinamento da equipe: R$ 10.000-25.000 uma vez
  • Custo total primeiro ano: R$ 40.000-110.000

Retornos esperados:

  • Redução de carga no banco de dados: 60-80% menos consultas ao banco primário
  • Melhoria no tempo de resposta: 3-5x APIs mais rápidas
  • Economia de infraestrutura: R$ 75.000-150.000 anualmente através de necessidades reduzidas de escalabilidade do banco
  • Produtividade do desenvolvedor: 20-30% de melhoria através de melhor performance do sistema
  • Experiência do usuário: Melhoria mensurável em retenção e engajamento

Cenários de ROI:

  • Conservador: 67% ROI no primeiro ano (payback de 8.6 meses)
  • Agressivo: 167% ROI no primeiro ano (payback de 4.5 meses)

Estratégia de migração do MongoDB

Abordagem de migração zero downtime

Fase 1: Implementação dual-write (semanas 1-2):

@Service
class MigrationCacheService(
private val mongoRepository: MongoRepository,
private val redisTemplate: RedisTemplate<String, Any>,
private val featureToggleService: FeatureToggleService
) {
suspend fun saveData(key: String, data: Any): Any {
// Escrita primária no MongoDB (sistema atual)
val savedData = mongoRepository.save(data)
// Escrita sombra no Redis (novo sistema)
if (featureToggleService.isDualWriteEnabled()) {
runCatching {
redisTemplate.opsForValue().set(key, savedData, Duration.ofHours(1))
}.onFailure {
logger.warn("Escrita Redis falhou para chave: $key", it)
}
}
return savedData
}
}

Fase 2: Migração gradual de leitura (semanas 3-4):

  • Começar com 10% dos usuários lendo do Redis
  • Aumentar 10% diariamente enquanto monitora performance
  • Cutover completo uma vez que validação confirma consistência de dados

Fase 3: Limpeza de cache MongoDB (semanas 5-6):

  • Remover lógica de cache MongoDB
  • Reter MongoDB para cenários de consulta complexos
  • Arquivar dados históricos de cache

Validação de migração e rollback

Serviço de validação automatizado:

@Service
class MigrationValidationService {
suspend fun validateMigration(): MigrationReport {
val inconsistencies = mutableListOf<String>()
var validatedKeys = 0L
var inconsistentKeys = 0L
mongoRepository.findAllCacheKeys().collect { key ->
val mongoData = mongoRepository.findByKey(key)
val redisData = redisTemplate.opsForValue().get(key)
when {
redisData == mongoData -> validatedKeys++
else -> {
inconsistentKeys++
inconsistencies.add(key)
}
}
}
return MigrationReport(
validatedKeys = validatedKeys,
inconsistentKeys = inconsistentKeys,
migrationPercentage = (validatedKeys.toDouble() /
(validatedKeys + inconsistentKeys)) * 100
)
}
}

Estratégias de mitigação de risco

Implementação de feature flag possibilita rollback seguro:

  • Capacidade de rollback instantâneo através de mudanças de configuração
  • Teste A/B de diferentes segmentos de usuário
  • Shifting gradual de tráfego baseado em métricas de performance

Monitoramento durante migração:

  • Comparação de performance em tempo real entre sistemas
  • Tracking de taxa de erro e alertas
  • Triggers de rollback automatizado baseados em degradação de performance

Considerações operacionais e monitoramento

Configuração de implantação em produção

Endurecimento de segurança Redis:

@Configuration
class RedisSecurityConfig {
@Bean
fun redisConnectionFactory(): LettuceConnectionFactory {
val sslOptions = SslOptions.builder()
.jdkSslProvider()
.build()
val clientConfig = LettuceClientConfiguration.builder()
.useSsl().sslOptions(sslOptions)
.commandTimeout(Duration.ofSeconds(2))
.build()
return LettuceConnectionFactory(
RedisStandaloneConfiguration().apply {
hostName = environment.getProperty("redis.host")
port = environment.getProperty("redis.port", Int::class.java) ?: 6379
password = RedisPassword.of(environment.getProperty("redis.password"))
},
clientConfig
)
}
}

Medidas de segurança essenciais:

  • Isolamento de rede através de VPC/subnets privadas
  • Criptografia TLS para dados em trânsito
  • Autenticação com senhas fortes ou acesso baseado em ACL
  • Restrições de comando para prevenir operações perigosas

Framework de monitoramento e alertas

Indicadores chave de performance para rastrear:

MétricaMetaThreshold de AlertaImpacto no Negócio
Taxa de hit de cache>80%<70%Degradação de performance
Latência média<2ms>5msImpacto na experiência do usuário
Utilização de memória<90%>95%Risco de eviction de cache
Taxa de erro<0.1%>1%Confiabilidade do serviço
Utilização do pool de conexão<80%>90%Planejamento de capacidade

Configuração de métricas Prometheus:

management:
metrics:
export:
prometheus:
enabled: true
distribution:
percentiles: 0.5, 0.95, 0.99
percentiles-histogram:
redis: true
endpoints:
web:
exposure:
include: "health,info,prometheus,metrics"

Runbooks operacionais

Estratégia de aquecimento de cache:

  • Aquecimento automatizado durante deployment
  • Aquecimento agendado para padrões de tráfego previsíveis
  • Aquecimento dirigido por eventos para conteúdo popular

Procedimentos de resposta a incidentes:

  • Fallback de falha de cache para consultas de banco de dados
  • Failover automatizado para instâncias de cache backup
  • Caminhos de escalação de degradação de performance

Diretrizes de planejamento de capacidade:

  • Monitorar tendências de crescimento de uso de memória
  • Escalar verticalmente antes da distribuição horizontal
  • Planejar para 3x capacidade de pico para lidar com spikes de tráfego

Recomendações específicas para 100k requisições/dia

Recomendação de arquitetura primária

AWS ElastiCache Redis configuração para seus requisitos específicos:

Especificação da instância:

  • Tipo: cache.t3.small (2 vCPUs, 2GB RAM)
  • Custo: Rê 75 operações = R$ 325/mês total
  • Capacidade: Lida com 100.000+ operações/segundo (1000x sua carga de pico)
  • Latência: Tempos de resposta sub-milissegundo
  • Disponibilidade: SLA 99.99% com deployment Multi-AZ

Otimizações de configuração:

# Otimização de memória para cargas de trabalho de cache
maxmemory 1500mb
maxmemory-policy allkeys-lru
maxmemory-samples 5
# Desabilitar persistência para máxima performance
save ""
appendonly no
# Otimização de conexão
timeout 300
tcp-keepalive 300
tcp-backlog 511
# Otimização de estrutura de memória
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2

Implementação de integração Kotlin:

@Configuration
@EnableCaching
class OptimizedRedisConfig {
@Bean
fun jedisConnectionFactory(): JedisConnectionFactory {
val poolConfig = JedisPoolConfig().apply {
maxTotal = 20
maxIdle = 10
minIdle = 2
testOnBorrow = true
testWhileIdle = true
}
return JedisConnectionFactory(
RedisStandaloneConfiguration("your-redis-endpoint", 6379),
poolConfig
)
}
@Bean
fun cacheManager(connectionFactory: RedisConnectionFactory): CacheManager {
val config = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(GenericJackson2JsonRedisSerializer()))
.entryTtl(Duration.ofHours(1))
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build()
}
}

Cronograma de implementação e marcos

Mês 1: Setup da fundação

  • Provisionamento da instância AWS ElastiCache Redis
  • Integração básica Kotlin com cache Spring Boot
  • Cache simples baseado em string para respostas de API
  • Configuração de monitoramento e alertas

Mês 2: Implementação avançada

  • Cache baseado em hash para dados estruturados
  • Cache de resultado computado com TTLs apropriados
  • Migração do cache MongoDB (fase dual-write)

Mês 3: Otimização de produção

  • Tuning de performance baseado em padrões de tráfego real
  • Endurecimento de segurança e estratégias de backup
  • Migração completa do cache MongoDB

Mês 4-6: Preparação para escalabilidade

  • Implementação de cache multi-tier
  • Monitoramento avançado e planejamento de capacidade
  • Documentação e conclusão de treinamento da equipe

Estratégias de otimização de custos

Economia de instâncias reservadas: 20-30% de redução adicional de custo através de compromissos de 1 ano

Considerações multi-região: Para tráfego global, implementar clusters Redis em múltiplas regiões AWS com roteamento inteligente

Backup e recuperação de desastre: Snapshots diários para S3 custam ~R$ 10-25/mês para proteção de dados

Otimização de custo de monitoramento: Usar monitoramento básico AWS CloudWatch (incluído) versus serviços de monitoramento premium

Conclusão e próximos passos

A pesquisa demonstra conclusivamente que o Redis representa a solução de cache ótima para seu serviço backend Kotlin. Com latência sub-milissegundo, margem de capacidade 100x maior, e eficiência de custo convincente a R$ 325/mês, o Redis aborda as limitações atuais de performance enquanto posiciona o sistema para crescimento futuro significativo.

Ordem de prioridade de implementação:

  1. Imediato: Implantar instância AWS ElastiCache Redis t3.small
  2. Semana 1-2: Implementar cache Redis básico para respostas de API
  3. Semana 3-4: Começar migração de cache MongoDB com estratégia dual-write
  4. Mês 2-3: Completar migração e implementar padrões avançados de cache
  5. Mês 3-6: Otimizar performance e preparar estratégias de escalabilidade

Resultados esperados em 90 dias:

  • Melhoria 3-5x nos tempos de resposta de API
  • Redução 60-80% na carga do banco de dados
  • Experiência do usuário aprimorada através de performance consistente
  • Base sólida de ROI com retornos de 67-167% projetados

O padrão de tráfego concentrado (8h-22h) alinha perfeitamente com as características de performance do Redis, enquanto a escala de 100k requisições/dia fornece margem significativa de crescimento sem requerer mudanças arquiteturais. Esta implementação estabelece uma fundação robusta para escalar para milhões de requisições diárias mantendo performance excepcional e eficiência de custos.

O ecossistema maduro do Redis, opções extensivas de integração Kotlin, e escalabilidade comprovada o tornam a escolha definitiva para arquiteturas modernas de cache. A estratégia de migração minimiza risco através de implementação gradual e monitoramento abrangente, garantindo continuidade de negócio durante toda a transição.

Apêndices

Apêndice A: Código de exemplo para implementação Redis-Kotlin

Configuração básica de conexão Redis:

@Configuration
@EnableCaching
class RedisConfiguration {
@Value("\${spring.redis.host:localhost}")
private lateinit var redisHost: String
@Value("\${spring.redis.port:6379}")
private var redisPort: Int = 6379
@Value("\${spring.redis.password:}")
private lateinit var redisPassword: String
@Bean
fun jedisConnectionFactory(): JedisConnectionFactory {
val redisStandaloneConfiguration = RedisStandaloneConfiguration().apply {
hostName = redisHost
port = redisPort
if (redisPassword.isNotBlank()) {
password = RedisPassword.of(redisPassword)
}
}
val jedisPoolConfig = JedisPoolConfig().apply {
maxTotal = 30
maxIdle = 20
minIdle = 5
testOnBorrow = true
testOnReturn = true
testWhileIdle = true
minEvictableIdleTimeMillis = 60000
timeBetweenEvictionRunsMillis = 30000
numTestsPerEvictionRun = 3
blockWhenExhausted = true
}
return JedisConnectionFactory(redisStandaloneConfiguration, jedisPoolConfig)
}
@Bean
fun redisTemplate(): RedisTemplate<String, Any> {
return RedisTemplate<String, Any>().apply {
connectionFactory = jedisConnectionFactory()
keySerializer = StringRedisSerializer()
valueSerializer = GenericJackson2JsonRedisSerializer()
hashKeySerializer = StringRedisSerializer()
hashValueSerializer = GenericJackson2JsonRedisSerializer()
afterPropertiesSet()
}
}
}

Serviço de cache genérico:

@Service
class CacheService(
private val redisTemplate: RedisTemplate<String, Any>,
private val objectMapper: ObjectMapper
) {
private val logger = LoggerFactory.getLogger(CacheService::class.java)
suspend fun <T> get(key: String, type: Class<T>): T? {
return try {
val value = redisTemplate.opsForValue().get(key)
when {
value == null -> null
type.isInstance(value) -> type.cast(value)
else -> objectMapper.convertValue(value, type)
}
} catch (e: Exception) {
logger.error("Erro ao buscar cache para chave: $key", e)
null
}
}
suspend fun set(key: String, value: Any, ttlMinutes: Long = 60) {
try {
redisTemplate.opsForValue().set(
key,
value,
Duration.ofMinutes(ttlMinutes)
)
} catch (e: Exception) {
logger.error("Erro ao salvar cache para chave: $key", e)
}
}
suspend fun delete(key: String): Boolean {
return try {
redisTemplate.delete(key)
} catch (e: Exception) {
logger.error("Erro ao deletar cache para chave: $key", e)
false
}
}
suspend fun exists(key: String): Boolean {
return try {
redisTemplate.hasKey(key)
} catch (e: Exception) {
logger.error("Erro ao verificar existência de chave: $key", e)
false
}
}
}

Implementação de cache para API responses:

@Service
class ProductCacheService(
private val cacheService: CacheService,
private val productRepository: ProductRepository
) {
private val logger = LoggerFactory.getLogger(ProductCacheService::class.java)
suspend fun getProduct(productId: String): Product? {
val cacheKey = "product:$productId"
// Tentar buscar do cache primeiro
cacheService.get(cacheKey, Product::class.java)?.let {
logger.debug("Cache hit para produto: $productId")
return it
}
// Cache miss - buscar do banco de dados
logger.debug("Cache miss para produto: $productId")
val product = productRepository.findById(productId)
// Salvar no cache se encontrado
product?.let {
cacheService.set(cacheKey, it, ttlMinutes = 30)
}
return product
}
suspend fun getProductsByCategory(categoryId: String): List<Product> {
val cacheKey = "products:category:$categoryId"
// Tentar buscar lista do cache
cacheService.get(cacheKey, object : TypeReference<List<Product>>() {}.type)?.let {
logger.debug("Cache hit para categoria: $categoryId")
return it as List<Product>
}
// Cache miss - buscar do banco
logger.debug("Cache miss para categoria: $categoryId")
val products = productRepository.findByCategoryId(categoryId)
// Cachear resultado (TTL menor para listas que mudam mais)
cacheService.set(cacheKey, products, ttlMinutes = 15)
return products
}
suspend fun invalidateProduct(productId: String) {
val productKey = "product:$productId"
cacheService.delete(productKey)
// Também invalidar caches relacionados se necessário
// Exemplo: categorias que contêm este produto
logger.info("Cache invalidado para produto: $productId")
}
}

Apêndice B: Scripts de monitoramento e alertas

Script de monitoramento de saúde Redis:

redis_health_check.sh
#!/bin/bash
REDIS_HOST=${1:-localhost}
REDIS_PORT=${2:-6379}
REDIS_PASSWORD=${3:-""}
# Função para verificar conectividade
check_redis_connectivity() {
if [ -n "$REDIS_PASSWORD" ]; then
redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD ping
else
redis-cli -h $REDIS_HOST -p $REDIS_PORT ping
fi
}
# Função para obter métricas
get_redis_metrics() {
if [ -n "$REDIS_PASSWORD" ]; then
redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD info
else
redis-cli -h $REDIS_HOST -p $REDIS_PORT info
fi
}
# Verificar conectividade
echo "Verificando conectividade Redis..."
PING_RESULT=$(check_redis_connectivity)
if [ "$PING_RESULT" != "PONG" ]; then
echo "ERRO: Redis não está respondendo"
exit 1
fi
echo "✓ Redis está operacional"
# Obter métricas importantes
echo "Coletando métricas..."
METRICS=$(get_redis_metrics)
# Extrair métricas específicas
USED_MEMORY=$(echo "$METRICS" | grep "used_memory:" | cut -d: -f2 | tr -d '\r')
USED_MEMORY_HUMAN=$(echo "$METRICS" | grep "used_memory_human:" | cut -d: -f2 | tr -d '\r')
CONNECTED_CLIENTS=$(echo "$METRICS" | grep "connected_clients:" | cut -d: -f2 | tr -d '\r')
TOTAL_COMMANDS=$(echo "$METRICS" | grep "total_commands_processed:" | cut -d: -f2 | tr -d '\r')
KEYSPACE_HITS=$(echo "$METRICS" | grep "keyspace_hits:" | cut -d: -f2 | tr -d '\r')
KEYSPACE_MISSES=$(echo "$METRICS" | grep "keyspace_misses:" | cut -d: -f2 | tr -d '\r')
# Calcular hit ratio
if [ "$KEYSPACE_HITS" -gt 0 ] || [ "$KEYSPACE_MISSES" -gt 0 ]; then
TOTAL_REQUESTS=$((KEYSPACE_HITS + KEYSPACE_MISSES))
HIT_RATIO=$(echo "scale=2; $KEYSPACE_HITS * 100 / $TOTAL_REQUESTS" | bc)
else
HIT_RATIO="N/A"
fi
# Exibir relatório
echo "==================== RELATÓRIO REDIS ===================="
echo "Memória Usada: $USED_MEMORY_HUMAN"
echo "Clientes Conectados: $CONNECTED_CLIENTS"
echo "Total de Comandos: $TOTAL_COMMANDS"
echo "Cache Hits: $KEYSPACE_HITS"
echo "Cache Misses: $KEYSPACE_MISSES"
echo "Taxa de Hit: $HIT_RATIO%"
echo "=========================================================="
# Alertas baseados em thresholds
if [ "$CONNECTED_CLIENTS" -gt 50 ]; then
echo "⚠️ ALERTA: Muitos clientes conectados ($CONNECTED_CLIENTS)"
fi
if [ "$HIT_RATIO" != "N/A" ] && [ $(echo "$HIT_RATIO < 70" | bc) -eq 1 ]; then
echo "⚠️ ALERTA: Taxa de hit baixa ($HIT_RATIO%)"
fi
echo "✓ Verificação de saúde concluída"

Configuração Prometheus para métricas Redis:

prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "redis_rules.yml"
scrape_configs:
- job_name: 'redis'
static_configs:
- targets: ['localhost:9121'] # Redis Exporter
scrape_interval: 5s
metrics_path: /metrics
- job_name: 'kotlin-app'
static_configs:
- targets: ['localhost:8080']
metrics_path: /actuator/prometheus
scrape_interval: 10s
alerting:
alertmanagers:
- static_configs:
- targets:
- localhost:9093

Regras de alerta para Redis:

redis_rules.yml
groups:
- name: redis_alerts
rules:
- alert: RedisDown
expr: redis_up == 0
for: 0m
labels:
severity: critical
annotations:
summary: "Redis instance está down"
description: "Redis instance {{ $labels.instance }} está inacessível"
- alert: RedisHighMemoryUsage
expr: redis_memory_used_bytes / redis_memory_max_bytes * 100 > 90
for: 2m
labels:
severity: warning
annotations:
summary: "Alto uso de memória Redis"
description: "Redis está usando {{ $value }}% da memória disponível"
- alert: RedisLowHitRatio
expr: rate(redis_keyspace_hits_total[5m]) / (rate(redis_keyspace_hits_total[5m]) + rate(redis_keyspace_misses_total[5m])) * 100 < 70
for: 5m
labels:
severity: warning
annotations:
summary: "Taxa de hit Redis baixa"
description: "Taxa de hit Redis é {{ $value }}% nos últimos 5 minutos"
- alert: RedisHighConnections
expr: redis_connected_clients > 100
for: 5m
labels:
severity: warning
annotations:
summary: "Muitas conexões Redis"
description: "Redis tem {{ $value }} clientes conectados"
- alert: RedisSlowQueries
expr: redis_slowlog_length > 10
for: 2m
labels:
severity: warning
annotations:
summary: "Consultas lentas Redis detectadas"
description: "Redis tem {{ $value }} consultas lentas no slowlog"

Apêndice C: Plano de capacidade e escalabilidade

Projeções de crescimento baseadas em métricas:

PeríodoRequisições/DiaRPS MédioRPS PicoInstância RecomendadaCusto Mensal (R$)
Atual100K1.166cache.t3.small325
6 meses250K2.915cache.t3.small325
1 ano500K5.830cache.t3.medium650
2 anos1M11.660cache.t3.medium650
3 anos2.5M29150cache.t3.large1.300
5 anos10M116600cache.r6g.xlarge3.900

Triggers para escalabilidade:

@Component
class CapacityMonitor(
private val meterRegistry: MeterRegistry,
private val alertService: AlertService
) {
@Scheduled(fixedRate = 300000) // A cada 5 minutos
fun checkCapacityMetrics() {
val metrics = collectRedisMetrics()
// Verificar uso de memória
if (metrics.memoryUsagePercent > 85) {
alertService.sendAlert(
"Redis Memory Warning",
"Uso de memória: ${metrics.memoryUsagePercent}% - Considere aumentar instância"
)
}
// Verificar throughput
if (metrics.operationsPerSecond > 50000) {
alertService.sendAlert(
"Redis Throughput Warning",
"Throughput: ${metrics.operationsPerSecond} ops/sec - Planeje escalabilidade"
)
}
// Verificar latência
if (metrics.averageLatencyMs > 2.0) {
alertService.sendAlert(
"Redis Latency Warning",
"Latência média: ${metrics.averageLatencyMs}ms - Investigue performance"
)
}
// Verificar taxa de hit
if (metrics.hitRatio < 0.8) {
alertService.sendAlert(
"Redis Hit Ratio Warning",
"Taxa de hit: ${metrics.hitRatio * 100}% - Revise estratégia de cache"
)
}
}
private fun collectRedisMetrics(): RedisMetrics {
// Implementação para coletar métricas do Redis
// Usando Redis INFO command ou cliente de monitoramento
return RedisMetrics(
memoryUsagePercent = 75.0,
operationsPerSecond = 25000,
averageLatencyMs = 1.2,
hitRatio = 0.85
)
}
}
data class RedisMetrics(
val memoryUsagePercent: Double,
val operationsPerSecond: Int,
val averageLatencyMs: Double,
val hitRatio: Double
)

Apêndice D: Checklist de migração

Pré-migração:

  • Ambiente Redis configurado e testado
  • Backup completo dos dados MongoDB atuais
  • Código de dual-write implementado e testado
  • Monitoramento configurado para ambos os sistemas
  • Plano de rollback documentado e testado
  • Equipe treinada nos novos procedimentos

Durante a migração:

  • Ativar dual-write (MongoDB + Redis)
  • Monitorar consistência de dados por 48-72h
  • Validar performance em ambiente de teste
  • Configurar alertas para inconsistências
  • Documentar problemas encontrados

Fase de transição:

  • Começar leitura Redis para 10% usuários
  • Incrementar gradualmente (10% por dia)
  • Monitorar métricas de performance
  • Validar funcionalidade crítica
  • Ajustar configurações conforme necessário

Pós-migração:

  • 100% das leituras via Redis
  • Remover código de dual-write
  • Limpeza de dados antigos MongoDB
  • Otimização final de performance
  • Documentação completa atualizada
  • Revisão de lições aprendidas

Apêndice E: Troubleshooting comum

Problemas frequentes e soluções:

  1. Alta latência repentina

    • Verificar uso de memória (>90% causa slowdown)
    • Analisar slowlog: SLOWLOG GET 10
    • Verificar configuração de rede/DNS
    • Revisar queries complexas ou grandes valores
  2. Taxa de hit baixa

    • Analisar padrões de TTL (muito baixo?)
    • Verificar estratégia de invalidação
    • Revisar tamanho do cache vs working set
    • Considerar warming de cache
  3. Problemas de conexão

    • Verificar pool de conexões (tamanho adequado?)
    • Validar timeout configurations
    • Checar limites de recursos (file descriptors)
    • Analisar logs de rede
  4. Consumo excessivo de memória

    • Revisar tipos de dados usados (hashes vs strings)
    • Implementar compressão para valores grandes
    • Ajustar políticas de eviction
    • Considerar particionamento de dados

Comandos úteis para debugging:

Terminal window
# Informações gerais
redis-cli info
# Monitorar comandos em tempo real
redis-cli monitor
# Verificar queries lentas
redis-cli slowlog get 10
# Analisar uso de memória por tipo
redis-cli --bigkeys
# Verificar configuração
redis-cli config get "*"
# Estatísticas de keyspace
redis-cli info keyspace

Este documento fornece a base completa para implementação bem-sucedida de Redis como solução de cache para seu backend Kotlin, incluindo todas as considerações técnicas, operacionais e de negócio necessárias para uma transição segura e eficiente.