Estratégias de cache: carregamento sob demanda e gravação simultânea
Implemente o carregamento sob demanda para preencher o cache quando houver falhas de consulta e a gravação simultânea para manter o cache consistente a cada gravação no banco de dados.
Estratégias de cache: carregamento sob demanda e gravação simultânea é uma aula grátis de AWS Solutions Architect no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AWS Solutions Architect, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AWS Solutions Architect inclui 4 aulas no total.
Por que as Estratégias de Cache são Importantes
Inserir um cache entre sua aplicação e o banco de dados exige uma estratégia de cache — um conjunto de regras que determina quando os dados são colocados no cache, quando são lidos do cache e quando são removidos. Escolher a estratégia errada resulta em dados desatualizados (o cache retorna valores antigos), falhas por cache frio (o cache está vazio e cada solicitação acessa o banco de dados) ou avalanche de cache (muitas solicitações simultâneas para a mesma chave ausente acessam o banco de dados ao mesmo tempo). As duas estratégias fundamentais são o carregamento lento e a gravação direta no cache.
Carregamento Lento (Cache-Aside): Como Funciona
O carregamento lento (também chamado de cache-aside) é o padrão de cache mais comum. A aplicação verifica primeiro o cache. Em um acerto de cache, os dados são retornados diretamente do cache — o caminho rápido. Em uma falha de cache, a aplicação busca os dados no banco de dados, grava o resultado no cache com um TTL e o retorna ao solicitante. O cache é preenchido somente com os dados que realmente são solicitados — daí o termo “lento”. A próxima solicitação da mesma chave a encontrará no cache.
# Lazy loading pattern (Python with redis-py)
def get_user(user_id, redis_client, db):
cache_key = f'user:{user_id}'
# 1. Check cache
cached = redis_client.get(cache_key)
if cached:
return json.loads(cached) # Cache HIT
# 2. Cache MISS: fetch from DB
user = db.query('SELECT * FROM users WHERE id = %s', user_id)
# 3. Populate cache with TTL of 300 seconds
redis_client.setex(cache_key, 300, json.dumps(user))
return userCarregamento Lento: Vantagens
O carregamento lento tem três vantagens principais: somente os dados solicitados são armazenados em cache — o cache não é preenchido com dados que ninguém lê, portanto a memória é usada com eficiência. Um cache frio não interrompe a aplicação — em caso de falhas de cache, a aplicação recorre ao banco de dados; assim, mesmo que o ElastiCache seja reiniciado ou um nó falhe, a aplicação continua funcionando, embora com maior latência. O cache é sempre eventualmente consistente com o banco de dados, porque os dados desatualizados expiram por meio do TTL, mesmo que algumas atualizações não tenham sido consideradas.
Carregamento Lento: Desvantagens
O carregamento lento tem três desvantagens principais: as falhas de cache são dispendiosas — são necessárias três operações (verificação do cache, leitura do banco de dados e gravação no cache), em comparação com uma em caso de acerto, o que causa maior latência nas solicitações de um cache frio. Dados desatualizados — após uma atualização no banco de dados, o cache continua fornecendo o valor antigo até o TTL expirar ou a chave ser explicitamente invalidada (janela de inconsistência do cache). Avalanche de cache — se uma chave popular expirar, muitas solicitações simultâneas terão uma falha de cache e acessarão o banco de dados ao mesmo tempo, podendo sobrecarregá-lo.
# Mitigating cache stampede with a probabilistic early expiration
# (Refresh the key before it expires to avoid simultaneous misses)
def get_with_stampede_protection(key, redis_client, db_fetch_fn, ttl=300):
value = redis_client.get(key)
ttl_remaining = redis_client.ttl(key)
# Probabilistically refresh before expiry
if value is None or (ttl_remaining < 30 and random.random() < 0.1):
value = db_fetch_fn()
redis_client.setex(key, ttl, json.dumps(value))
return json.loads(value)Gravação Direta no Cache: Como Funciona
Na estratégia de gravação direta no cache, cada gravação no banco de dados também é gravada simultaneamente no cache. A aplicação grava tanto no cache quanto no banco de dados como parte da mesma operação (ou o banco de dados aciona uma atualização do cache). Dessa forma, o cache está sempre sincronizado com o banco de dados — não há uma janela de dados desatualizados. A gravação direta no cache garante que os dados armazenados estejam sempre atualizados, fazendo com que as leituras seguintes de dados gravados recentemente sejam sempre acertos de cache.
# Write-through pattern (Python)
def update_user(user_id, user_data, redis_client, db):
# 1. Write to database FIRST
db.execute('UPDATE users SET name=%s WHERE id=%s',
(user_data['name'], user_id))
# 2. Update cache immediately (write-through)
cache_key = f'user:{user_id}'
redis_client.setex(cache_key, 3600, json.dumps(user_data))
return user_data
# Every read is now a cache hit for recently updated dataGravação Direta no Cache: Vantagens
Vantagens da gravação direta no cache: os dados do cache estão sempre atualizados — não há dados desatualizados, pois o cache é atualizado a cada gravação. As leituras são sempre rápidas — os dados populares gravados com frequência estão sempre no cache. Não há avalanche de cache nas leituras — como os dados são pré-carregados antes de serem lidos, não há falhas por cache frio para dados gravados recentemente. Essa estratégia é ideal para cargas de trabalho com predominância de leitura e atualizações frequentes, nas quais a atualização dos dados é essencial, como catálogos de produtos, sistemas de preços ou caches de perfis de usuários.
Gravação Direta no Cache: Desvantagens
Desvantagens da gravação direta no cache: penalidade de gravação — cada gravação envolve duas operações (banco de dados + cache), aumentando a latência das gravações. Poluição do cache — os dados são armazenados mesmo que nunca sejam lidos novamente (gravados uma vez e nunca solicitados), desperdiçando memória do cache. A reinicialização do cache resulta em um cache frio — se o cluster de cache for reiniciado, todos os dados gravados proativamente serão perdidos, e o cache deverá ser preenchido novamente por meio de gravações ou de um processo de aquecimento do cache. Combine a gravação direta no cache com um TTL para evitar o crescimento ilimitado de dados armazenados que raramente são lidos.
Combinando Carregamento Lento e Gravação Direta no Cache
Na prática, muitos sistemas de produção combinam as duas estratégias: use a gravação direta no cache para dados atualizados e lidos com frequência (como sessões de usuários ou preços atuais) e o carregamento lento para dados raramente atualizados e lidos com frequência (como descrições de produtos ou conteúdo de artigos). Defina TTLs apropriados para ambos: as chaves gravadas diretamente no cache recebem um TTL longo, pois os dados estão sempre atualizados; as chaves carregadas lentamente recebem um TTL mais curto para limitar a janela de dados desatualizados. Essa abordagem híbrida maximiza a taxa de acertos do cache e minimiza a desatualização.
# Hybrid: write-through for sessions, lazy loading for product data
# Write-through for session data (always fresh, critical)
def save_session(session_id, data, redis_client, db):
db.upsert('sessions', session_id, data)
redis_client.setex(f'session:{session_id}', 3600, json.dumps(data))
# Lazy loading for product catalog (infrequent updates OK)
def get_product(product_id, redis_client, db):
cached = redis_client.get(f'product:{product_id}')
if cached:
return json.loads(cached)
product = db.query('SELECT * FROM products WHERE id=%s', product_id)
redis_client.setex(f'product:{product_id}', 86400, json.dumps(product))
return productPrincípios de Projeto de TTL
O Time-To-Live (TTL) dos dados armazenados em cache controla a janela máxima de dados desatualizados e o uso de memória do cache. Defina os TTLs com base na: frequência de atualização dos dados (os dados de sessão mudam com frequência → TTL curto; o conteúdo estático muda raramente → TTL longo), tolerância à desatualização (preços financeiros → muito curto; texto de uma publicação de blog → horas ou dias) e capacidade de memória do cache (pouca memória → TTL mais curto para remover dados desatualizados mais rapidamente). Sempre defina um TTL — nunca armazene dados sem expiração, pois o cache acabará ficando cheio de dados desatualizados.
# TTL examples by data type
# API rate limit counter: 60 seconds
redis_client.setex(f'ratelimit:{ip}', 60, count)
# User session: 30 minutes
redis_client.setex(f'session:{id}', 1800, json.dumps(session))
# Product catalog: 24 hours
redis_client.setex(f'product:{id}', 86400, json.dumps(product))
# Stock price: 10 seconds
redis_client.setex(f'price:{symbol}', 10, price)
# Static site content: 7 days
redis_client.setex(f'page:{slug}', 604800, html_content)Invalidação do Cache na Atualização
Em vez de depender somente da expiração do TTL, você pode invalidar explicitamente (excluir) as chaves do cache quando os dados subjacentes forem alterados. Isso elimina completamente a janela de dados desatualizados. Padrões comuns: exclusão na gravação (excluir a chave do cache após cada atualização do banco de dados — a próxima leitura a preenche novamente por meio do carregamento lento) e invalidação orientada por eventos (o DynamoDB Streams ou a captura de alterações do RDS aciona uma função Lambda que exclui as chaves afetadas). A invalidação do cache é um dos problemas mais difíceis em sistemas distribuídos; quanto mais simples for a lógica de invalidação, mais confiável será o seu cache.
# Delete-on-write invalidation pattern
def update_product(product_id, new_data, redis_client, db):
# Update the database
db.execute('UPDATE products SET ... WHERE id=%s', (product_id,))
# Invalidate the cache key
redis_client.delete(f'product:{product_id}')
# Also invalidate any list/search caches that may include this product
redis_client.delete('products:list:page:1')
redis_client.delete(f'products:category:{new_data["category_id"]}')
# Next read will trigger lazy loading with fresh dataArmazenamento em Cache com Gravação Posterior (Write-Back)
Um padrão menos comum, mas poderoso, é a gravação posterior (write-back): a aplicação grava somente no cache, e o cache descarrega os dados de forma assíncrona para o banco de dados em segundo plano. Isso proporciona gravações extremamente rápidas (somente em memória), ao custo de uma possível perda de dados se o cache falhar antes de descarregá-los. A gravação posterior é apropriada para gravações de alta frequência e grande volume, nas quais os dados podem ser reconstruídos ou pequenas perdas são aceitáveis — como contadores de acertos, eventos de análise ou atualizações de pontuações de jogos. O ElastiCache não oferece suporte nativo à gravação posterior; ela deve ser implementada na camada da aplicação.
# Write-behind pattern: write to cache, flush to DB asynchronously
# Application writes:
# redis_client.incr('post:42:views') # fast, in-memory only
# Background job (runs every 60 seconds):
def flush_view_counts(redis_client, db):
for key in redis_client.scan_iter('post:*:views'):
count = redis_client.getdel(key) # atomic get-and-delete
post_id = key.split(':')[1]
db.execute('UPDATE posts SET views = views + %s WHERE id = %s',
(int(count), post_id))Verificação Rápida
Teste sua compreensão dos conceitos do AWS Solutions Architect (SAA-C03) apresentados nesta lição.
Recapitulação da Lição
Nesta lição, você aprendeu que: o carregamento lento preenche o cache após falhas de leitura e usa a memória com eficiência, mas pode fornecer dados desatualizados até o TTL expirar; a gravação direta no cache atualiza o cache a cada gravação, eliminando dados desatualizados, mas desperdiça memória com dados não lidos; e a invalidação explícita exclui as chaves do cache após atualizações no banco de dados para eliminar as janelas de dados desatualizados. A seguir, exploraremos o armazenamento de sessões e os padrões de tabelas de classificação com o ElastiCache.
Perguntas Frequentes
A aula “Estratégias de cache: carregamento sob demanda e gravação simultânea” é grátis?
Sim — o texto completo de “Estratégias de cache: carregamento sob demanda e gravação simultânea” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AWS Solutions Architect, atualize para CoddyKit PRO. O curso de AWS Solutions Architect inclui 4 aulas no total.
O que vou aprender em “Estratégias de cache: carregamento sob demanda e gravação simultânea”?
Implemente o carregamento sob demanda para preencher o cache quando houver falhas de consulta e a gravação simultânea para manter o cache consistente a cada gravação no banco de dados. Você pratica AWS Solutions Architect com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar AWS Solutions Architect?
Nenhuma experiência prévia é necessária. AWS Solutions Architect no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.
Quanto tempo leva a aula “Estratégias de cache: carregamento sob demanda e gravação simultânea”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de AWS Solutions Architect?
Sim. Cada aula de AWS Solutions Architect inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Redis versus Memcached: Escolha do mecanismo adequado
- Grupos de replicação e modo de cluster do ElastiCache Redis
- Estratégias de cache: carregamento sob demanda e gravação simultânea
- Armazenamento de sessões e padrões de classificação