Memorystore é o serviço de cache em memória gerenciado do GCP. Oferece Redis e Memcached totalmente gerenciados, sem necessidade de gerenciar nós, patches ou replicação.
Todos os dados ficam em RAM, com latência de leitura/escrita na casa de microssegundos. Instâncias são acessíveis somente por IP privado dentro da VPC.
Redis vs Memcached
| Aspecto | Redis | Memcached |
|---|---|---|
| Estruturas de dados | Strings, Hashes, Lists, Sets, Sorted Sets, Streams | Apenas strings |
| Replicação | Sim (Standard tier) | Não |
| Persistência | Opcional (RDB/AOF) | Não |
| Pub/Sub | Sim | Não |
| Lua scripting | Sim | Não |
| Quando usar | Cache avançado, sessões, filas, rate limiting, leaderboards | Cache simples de alta velocidade e baixo custo |
Para novos projetos, prefira Redis: funcionalidades muito mais ricas.
Memorystore for Redis
Tiers
| Tier | HA | Persistência | Recomendado |
|---|---|---|---|
| Basic | Não | Não | Dev/testing, cache descartável |
| Standard | Sim (réplica automática, failover ~1s) | Opcional | Produção |
Criar instância
# Instância Standard com 5 GB (produção)
gcloud redis instances create meu-redis \
--size=5 \
--region=southamerica-east1 \
--zone=southamerica-east1-a \
--replica-zone=southamerica-east1-b \
--tier=STANDARD \
--redis-version=redis_7_2 \
--network=projects/PROJETO/global/networks/minha-vpc
# Obter IP de conexão
gcloud redis instances describe meu-redis \
--region=southamerica-east1 \
--format="value(host)"Conexão
import redis
# Conexão básica (dentro da VPC)
r = redis.Redis(host="10.x.x.x", port=6379, decode_responses=True)
# Com autenticação (AUTH string configurada na instância)
r = redis.Redis(
host="10.x.x.x",
port=6379,
password="minha-senha",
decode_responses=True
)
r.ping() # TruePadrões comuns
import json, time, functools
# Cache de resultado de função com TTL
def cache_redis(ttl=300):
def decorator(func):
@functools.wraps(func)
def wrapper(*args):
key = f"{func.__name__}:{':'.join(str(a) for a in args)}"
cached = r.get(key)
if cached:
return json.loads(cached)
result = func(*args)
r.setex(key, ttl, json.dumps(result))
return result
return wrapper
return decorator
@cache_redis(ttl=600)
def buscar_produto(produto_id: str) -> dict:
return db.query("SELECT * FROM produtos WHERE id = %s", produto_id)
# Sessão de usuário (Hash com TTL)
session_id = "sess-abc123"
r.hset(f"session:{session_id}", mapping={
"user_id": "123",
"email": "[email protected]"
})
r.expire(f"session:{session_id}", 3600)
dados_sessao = r.hgetall(f"session:{session_id}")
# Rate limiting (janela de 1 minuto)
def verificar_rate_limit(ip: str, limite: int = 100) -> bool:
key = f"rate:{ip}:{int(time.time() // 60)}"
count = r.incr(key)
if count == 1:
r.expire(key, 60)
return count <= limite
# Fila simples com List (FIFO)
r.lpush("fila:emails", json.dumps({"para": "[email protected]", "assunto": "Bem-vindo"}))
item_raw = r.brpop("fila:emails", timeout=5) # bloqueia até ter item ou timeout
if item_raw:
item = json.loads(item_raw[1])
# Leaderboard com Sorted Set
r.zadd("ranking:global", {"jogador-A": 1500.0, "jogador-B": 2000.0, "jogador-C": 1750.0})
top_10 = r.zrevrange("ranking:global", 0, 9, withscores=True)
posicao = r.zrevrank("ranking:global", "jogador-A")
# Pub/Sub
# Publisher
r.publish("canal:pedidos", json.dumps({"tipo": "pedido_criado", "id": "123"}))
# Subscriber (em outra thread/processo)
pubsub = r.pubsub()
pubsub.subscribe("canal:pedidos")
for message in pubsub.listen():
if message["type"] == "message":
evento = json.loads(message["data"])
processar(evento)
# Contador atômico
r.incr("visitas:home")
r.incrby("estoque:produto-X", -1)Políticas de eviction
Quando a memória está cheia, o Redis descarta chaves conforme a política configurada:
| Política | Comportamento | Quando usar |
|---|---|---|
noeviction | Retorna erro, não descarta | Cache crítico que não pode perder dados |
allkeys-lru | Remove chaves menos usadas recentemente | Cache geral (recomendado) |
volatile-lru | Remove chaves com TTL, priorizando LRU | Mix de cache e dados persistentes |
allkeys-lfu | Remove chaves com menor frequência de uso | Cargas com hotkeys muito acessadas |
volatile-ttl | Remove chaves com TTL menor | Quando TTL representa prioridade de descarte |
gcloud redis instances update meu-redis \
--region=southamerica-east1 \
--update-redis-config=maxmemory-policy=allkeys-lruImport / Export
# Exportar snapshot RDB para GCS
gcloud redis instances export gs://meu-bucket/redis-backup.rdb meu-redis \
--region=southamerica-east1
# Importar de GCS (instância deve estar vazia)
gcloud redis instances import gs://meu-bucket/redis-backup.rdb meu-redis \
--region=southamerica-east1Redis Cluster (Memorystore for Redis Cluster)
Para workloads que precisam de > 300 GB ou altíssimo throughput de escrita, o Memorystore oferece Redis Cluster com sharding nativo:
gcloud redis clusters create meu-cluster-redis \
--shard-count=3 \
--replica-count=1 \
--region=southamerica-east1 \
--network=projects/PROJETO/global/networks/minha-vpcO cliente precisa usar um cliente compatível com Redis Cluster:
from redis.cluster import RedisCluster
rc = RedisCluster(host="10.x.x.x", port=6379, decode_responses=True)
rc.set("chave", "valor")Memorystore for Memcached
Cache distribuído nativo: múltiplos nós independentes com hash consistente pelo cliente.
gcloud memcache instances create meu-memcached \
--node-count=3 \
--node-cpu=1 \
--node-memory=1GB \
--region=southamerica-east1
# Obter IPs dos nós
gcloud memcache instances describe meu-memcached --region=southamerica-east1from pymemcache.client.hash import HashClient
client = HashClient([
("10.0.0.1", 11211),
("10.0.0.2", 11211),
("10.0.0.3", 11211),
])
client.set("chave", b"valor", expire=300)
valor = client.get("chave")Monitoramento
- Cloud Monitoring:
redis.googleapis.com/stats/memory/usage_ratio,redis.googleapis.com/stats/keyspace_hits - Taxa de hit/miss:
keyspace_hits / (keyspace_hits + keyspace_misses)(ideal > 95%) - Monitorar
connected_clientseblocked_clients(filas travadas)
Ver também: gcp | gcp-cloud-functions | gcp-firestore | gcp-cloud-sql | gcp-boas-praticas