Cache para Serviços 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étrica | Performance Redis | Performance MongoDB | Vantagem |
---|---|---|---|
Operações/segundo | 100.000+ | 20.000-40.000 | 3-5x maior |
Latência P50 | 0.16ms | 5ms | 30x mais rápido |
Latência P99 | <2ms | 15-23ms | 10x mais rápido |
Overhead de memória | 5.7GB por 2.5M registros | 2.7GB por 2.5M registros | MongoDB 2x mais eficiente |
Capacidade de escala | Linear até 1M+ ops/sec | Degrada além de 40k ops/sec | Redis 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 individuaisjedis.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ãojedis.setex("api:product:456", 3600, jsonResponse)
Otimização de configuração para seu caso de uso:
# Desabilitar persistência para máxima performance de cachesave ""appendonly no
# Otimização de memóriamaxmemory-policy allkeys-lruhash-max-ziplist-entries 512hash-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+
@Configurationclass 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ção | Performance | Simplicidade | Custo | Distribuído | Melhor 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
@Serviceclass 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:
@Componentclass 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:
@Componentclass 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.
Provedor | Serviço | Configuração | Custo Mensal | Custo Anual |
---|---|---|---|---|
AWS | ElastiCache Redis | cache.t3.small | R | R$ 3.900 |
Cloud Memorystore | 1GB Standard | R | R$ 3.000 | |
Azure | Cache for Redis | Standard C1 | R | R$ 2.520 |
Redis Cloud | Essentials | 1GB instance | R | 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):
@Serviceclass 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:
@Serviceclass 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:
@Configurationclass 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étrica | Meta | Threshold de Alerta | Impacto no Negócio |
---|---|---|---|
Taxa de hit de cache | >80% | <70% | Degradação de performance |
Latência média | <2ms | >5ms | Impacto 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 cachemaxmemory 1500mbmaxmemory-policy allkeys-lrumaxmemory-samples 5
# Desabilitar persistência para máxima performancesave ""appendonly no
# Otimização de conexãotimeout 300tcp-keepalive 300tcp-backlog 511
# Otimização de estrutura de memóriahash-max-ziplist-entries 512hash-max-ziplist-value 64list-max-ziplist-size -2
Implementação de integração Kotlin:
@Configuration@EnableCachingclass 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:
- Imediato: Implantar instância AWS ElastiCache Redis t3.small
- Semana 1-2: Implementar cache Redis básico para respostas de API
- Semana 3-4: Começar migração de cache MongoDB com estratégia dual-write
- Mês 2-3: Completar migração e implementar padrões avançados de cache
- 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@EnableCachingclass 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:
@Serviceclass 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:
@Serviceclass 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:
#!/bin/bashREDIS_HOST=${1:-localhost}REDIS_PORT=${2:-6379}REDIS_PASSWORD=${3:-""}
# Função para verificar conectividadecheck_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étricasget_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 conectividadeecho "Verificando conectividade Redis..."PING_RESULT=$(check_redis_connectivity)
if [ "$PING_RESULT" != "PONG" ]; then echo "ERRO: Redis não está respondendo" exit 1fi
echo "✓ Redis está operacional"
# Obter métricas importantesecho "Coletando métricas..."METRICS=$(get_redis_metrics)
# Extrair métricas específicasUSED_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 ratioif [ "$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órioecho "==================== 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 thresholdsif [ "$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:
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:
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íodo | Requisições/Dia | RPS Médio | RPS Pico | Instância Recomendada | Custo Mensal (R$) |
---|---|---|---|---|---|
Atual | 100K | 1.16 | 6 | cache.t3.small | 325 |
6 meses | 250K | 2.9 | 15 | cache.t3.small | 325 |
1 ano | 500K | 5.8 | 30 | cache.t3.medium | 650 |
2 anos | 1M | 11.6 | 60 | cache.t3.medium | 650 |
3 anos | 2.5M | 29 | 150 | cache.t3.large | 1.300 |
5 anos | 10M | 116 | 600 | cache.r6g.xlarge | 3.900 |
Triggers para escalabilidade:
@Componentclass 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:
-
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
-
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
-
Problemas de conexão
- Verificar pool de conexões (tamanho adequado?)
- Validar timeout configurations
- Checar limites de recursos (file descriptors)
- Analisar logs de rede
-
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:
# Informações geraisredis-cli info
# Monitorar comandos em tempo realredis-cli monitor
# Verificar queries lentasredis-cli slowlog get 10
# Analisar uso de memória por tiporedis-cli --bigkeys
# Verificar configuraçãoredis-cli config get "*"
# Estatísticas de keyspaceredis-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.