Práticas recomendadas do Memorystore para Redis

Nesta página, você encontra orientação sobre o uso ideal do Memorystore para Redis. Essa página também indica possíveis problemas que devem ser evitados.

Para ver uma lista de cenários de solução de problemas, consulte Solução de problemas.

Práticas recomendadas de alta disponibilidade e durabilidade de dados

Recomendamos usar o nível Standard como o mecanismo principal para alta disponibilidade (HA). O nível Standard oferece uma instância de alta disponibilidade com várias réplicas e permite recuperação rápida usando failover automático se a primária falhar. Além disso, ativar snapshots de RDB em instâncias do nível Standard oferece proteção extra contra falhas de nós.

Em alguns casos, talvez você também queira garantir que é possível recuperar dados de backups de instantâneos. Nesses cenários, os backups e a capacidade de restaurar dados de snapshots do RDB podem oferecer proteção adicional contra perda de dados.

Se você ativar os snapshots do RDB e precisar recuperar seus dados, o Memorystore para Redis poderá fazer isso com base no snapshot mais recente. Esses snapshots são apenas para recuperação interna do sistema e não podem ser acessados.

Limitações de durabilidade e práticas recomendadas

Embora o nível Standard ofereça alta disponibilidade por replicação e failover automático, ela não garante a durabilidade total dos dados. O Memorystore para Redis não pode oferecer durabilidade total dos dados, mesmo com a alta disponibilidade ativada durante determinados eventos catastróficos, como falhas regionais temporárias.

Para ajudar a proteger seus dados e fornecer um nível básico de persistência, os snapshots de RDB são ativados por padrão quando você cria uma instância do Memorystore for Redis usando o Google Cloud console.

Para cargas de trabalho de produção, considere as seguintes práticas recomendadas para melhorar ainda mais a resiliência dos dados:

  • Recuperação de desastres: o Memorystore para Redis não é compatível com a replicação entre regiões. Se a sua estratégia de recuperação de desastres exigir replicação entre regiões para uma resiliência regional real, recomendamos usar uma instância do Memorystore for Valkey. Para mais informações sobre como configurar e gerenciar instâncias secundárias em várias regiões, consulte Trabalhar com a replicação entre regiões na documentação do Memorystore for Valkey.
  • Durabilidade máxima: se o aplicativo exigir o nível mais alto possível de durabilidade dos dados, recomendamos usar uma instância do Memorystore for Valkey configurada com replicação entre regiões, backups diários automatizados e persistência de arquivo somente de anexação (AOF, na sigla em inglês). Essa combinação oferece a proteção mais robusta contra perda de dados. Para mais informações, consulte Visão geral da persistência na documentação do Memorystore for Valkey.

Exportação de RDB

Ao exportar um backup do RDB, siga estas orientações:

Operações com uso intensivo de recursos

Para instâncias do Redis de nível Padrão, as seguintes operações usam memória extra durante a operação:

O upgrade, o escalonamento e o failover manual de versões usam memória extra (para instâncias do nível Padrão) devido à replicação. Essas operações seguem o processo de replicação descrito em Comportamento de upgrade da instância do nível Padrão.

As operações de importação e exportação exigem memória extra devido ao processo Redis interrompido e ao gerenciamento de dados de cópia em gravação associados a essas operações.

Para diminuir as desvantagens das operações que usam muitos recursos, é necessário:

Operações e cenários que exigem uma nova tentativa de conexão

As operações e os cenários a seguir interrompem a conexão de rede entre sua rede e a instância do Redis:

Essas operações modificam a instância, exigindo uma interrupção de conexão temporária. A lógica de nova tentativa precisa estar em espera exponencial antes da execução dessas operações para que o aplicativo se reconecte automaticamente e continue funcionando normalmente.

Manutenção de rotina

As instâncias do Memorystore para Redis passam por manutenção periodicamente. Para mais detalhes, consulte a política de manutenção do Memorystore para Redis.

Implemente as práticas recomendadas a seguir para se preparar para manutenção de rotina:

Gerenciamento de memória

O gerenciamento de memória pode ser um desafio devido à fragmentação de memória bem conhecida que ocorre com o Redis de código aberto. Recomendamos que você reduza a configuração de maxmemory da instância para se manter sobrecarregado no caso de alta pressão da memória.

A melhor maneira de monitorar a pressão sobre a memória na instância do Memorystore é usar a métrica de proporção de uso da memória do sistema. Para um guia detalhado sobre como gerenciar a memória do Memorystore para Redis, consulte Práticas recomendadas de gerenciamento de memória.

Como gerenciar conexões inativas

Com o tempo, é possível que o número de conexões com sua instância do Memorystore aumente se as conexões não estiverem sendo encerradas corretamente. Isso pode ter implicações negativas no desempenho, especialmente se você estiver usando criptografia em trânsito, que impõe limites máximos de conexões com base no seu nível de capacidade. Para atenuar esse problema, recomendamos o uso do parâmetro de configuração do Redis timeout, que permite definir o número de segundos antes que as conexões de clientes inativas sejam encerradas automaticamente.

Nomes de recursos da Transparência no acesso

Dados sensíveis não devem ser armazenados em nomes de recursos do Memorystore for Redis. Por nomes de recursos, queremos dizer nomes de instâncias do Memorystore para Redis e metadados de instâncias, como tags. Não há garantia de que os dados armazenados em nomes de recursos sejam protegidos pela Google Cloud Transparência no acesso, e eles podem entrar em conflito com os requisitos de compliance da sua organização.

Conector de acesso VPC sem servidor necessário em alguns ambientes sem servidor

Alguns ambientes sem servidor exigem um conector de acesso VPC sem servidor para se conectar ao Memorystore para Redis. Se quiser se conectar usando um desses ambientes, configure o conector de acesso VPC sem servidor para seu projeto.

Rede

Recomendamos que você use o acesso a serviços particulares no modo de conexão. O Memorystore para Redis usa dois modos de conexão: acesso privado a serviços e peering direto. O modo de conexão de acesso a serviços particulares simplifica o gerenciamento de intervalos de IP e permite usar a VPC compartilhada, se quiser.

Depois de criar uma instância, não é possível alterar o modo de conexão.

Para mais detalhes, consulte Rede.

Monitoramento e alertas

Recomendamos o uso de monitoramento e alertas, porque eles fornecem indicadores-chave sobre o uso da memória da sua instância do Redis. Elas também fornecem insights sobre a eficiência da sua instância do Redis na resposta às solicitações de cache recebidas.

É necessário configurar os seguintes alertas padrão:

Práticas recomendadas para uso da CPU

O uso inadequado de comandos caros do Redis leva a alta latência, falta de resposta ou problemas de conectividade. As instâncias do nível Standard oferecem alta disponibilidade durante a recuperação de desastres e dependem da replicação assíncrona entre os nós primário e de réplica. Se um dos nós tiver um processamento de comandos caro que bloqueia a linha de execução principal do Redis, a replicação poderá ser afetada. Se o problema persistir e ocorrer uma interrupção em um local, os dados mais recentes gravados no local da interrupção poderão não estar disponíveis no outro local.

Recomendamos usar o Cloud Monitoring para definir alertas para a métrica Segundos de CPU da linha de execução principal (redis.googleapis.com/stats/cpu_utilization_main_thread) e garantir que o uso da CPU não exceda 0,8 segundo para o nó principal ou 0,5 segundo para cada nó de réplica, quando a réplica é designada como uma réplica de leitura.

Se a instância do Redis exceder os valores recomendados, escale a instância para um nível de capacidade mais alto ou siga as instruções de solução de problemas para evitar operações que consomem muita CPU.

Se a instância tiver alta utilização da CPU ou os recursos dela se esgotarem (por exemplo, por ter muitas conexões), a instância poderá apresentar um comportamento inadequado e as métricas externas poderão estar ausentes.

Comandos com uso intensivo de recursos

Recomendamos evitar o uso de comandos do Redis que exigem muitos recursos. O uso desses comandos pode resultar nos seguintes problemas de desempenho:

  • Alta latência e tempos limite do cliente
  • Pressão na memória causada por comandos que aumentam o uso da memória
  • Perda de dados durante a replicação e sincronização de nós porque a linha de execução principal do Redis está bloqueada
  • Verificações de integridade, capacidade de observação e replicação insuficientes

A tabela a seguir lista exemplos de comandos do Redis que consomem muitos recursos e oferece alternativas mais eficientes.

Categoria Comando que exige muitos recursos Alternativa eficiente em recursos
Executar para todo o keyspace KEYS SCAN
Executar para um conjunto de chaves de comprimento variável LRANGE Limite o tamanho do intervalo usado em uma consulta.
ZRANGE Limite o tamanho do intervalo usado em uma consulta.
HGETALL HSCAN
SMEMBERS SSCAN
Bloquear a execução de um script EVAL Verifique se o script não é executado indefinidamente.
EVALSHA Verifique se o script não é executado indefinidamente.
Remover arquivos e links DEL UNLINK
Publicar e assinar PUBLISH SPUBLISH
SUBSCRIBE SSUBSCRIBE

Práticas recomendadas para clientes Redis

Esta seção fornece orientações sobre como usar o cliente Redis de maneira ideal.

Detectar e processar conexões sem resposta

Recomendamos configurar o aplicativo cliente para detectar conexões sem resposta com a Memorystore para Redis. Quando uma conexão sem resposta é detectada, o cliente precisa redefini-la. Para criar um aplicativo resiliente, recomendamos as seguintes configurações de cliente:

  • Configure os parâmetros de sinal de atividade do TCP: defina os parâmetros TCP keepalive time, TCP keepalive interval e TCP keepalive probes para que os clientes detectem e descartem conexões sem resposta de maneira proativa, mesmo quando elas estão inativas. Por exemplo, se você definir o parâmetro TCP keepalive time como 30 segundos, TCP keepalive interval como 10 segundos e TCP keepalive probes como 3, os clientes vão redefinir as conexões inativas sem resposta em um minuto.
  • Configure tempos limite de usuário do TCP: defina esse tempo limite nos clientes para redefinir conexões com solicitações pendentes e parar de responder. Por exemplo, se você definir o tempo limite como 15 segundos, os clientes vão redefinir as conexões sem resposta que tiverem solicitações pendentes após 15 segundos.

Práticas recomendadas específicas para clientes

Se você escalonar horizontalmente seus aplicativos, poderá ter erros READONLY em comandos de gravação. O Memorystore para Redis usa uma implantação não Sentinel com endpoints estáticos, e os clientes não recebem informações de função dinâmicas antecipadamente. Se você usar uma única conexão de cliente para os endpoints principal e de réplica, o cliente poderá enviar comandos de gravação por engano para a réplica somente leitura. A réplica retorna um erro READONLY porque não consegue processar comandos de gravação.

Para evitar erros de roteamento de gravação, não use uma única conexão para leituras e gravações. Em vez disso, divida as operações criando as seguintes instâncias de cliente separadas:

  • Cliente principal: conecta-se apenas ao endpoint principal.
  • Cliente de réplica: conecte-se apenas ao endpoint de réplica.

As guias a seguir mostram como configurar os modelos separados em Go, Java, Node.js e Python.

Go

package main

import (
  "context"
  "fmt"

  "github.com/redis/go-redis/v9"
)

func main() {
ctx := context.Background()

  // Initialize the primary client connecting only to the primary endpoint
  primaryClient := redis.NewClient(&redis.Options{
      Addr:     "PRIMARY_HOST:6379",
      Password: "YOUR_PASSWORD",
      DB:       0,
  })
  defer primaryClient.Close()

  // Initialize the replica client connecting only to the replica endpoint
  replicaClient := redis.NewClient(&redis.Options{
      Addr:     "REPLICA_HOST:6379",
      Password: "YOUR_PASSWORD",
      DB:       0,
  })
  defer replicaClient.Close()

  // Use the primary client for all mutating commands
  err := primaryClient.Set(ctx, "example_key", "example_value", 0).Err()
  if err != nil {
      fmt.Printf("Failed to write to primary: %v\n", err)
  }

  // Use the replica client for all read-only commands
  val, err := replicaClient.Get(ctx, "example_key").Result()
  if err != nil {
      fmt.Printf("Failed to read from replica: %v\n", err)
  } else {
      fmt.Printf("Successfully read value: %s\n", val)
  }
}

Java

@Bean
public RedisConnectionFactory primaryConnectionFactory() {
  RedisStandaloneConfiguration config = new RedisStandaloneConfiguration("PRIMARY_HOST", 6379);
  config.setPassword(RedisPassword.of("YOUR_PASSWORD"));
  return new LettuceConnectionFactory(config);
}

@Bean
public RedisConnectionFactory replicaConnectionFactory() {
  RedisStaticMasterReplicaConfiguration config =
      new RedisStaticMasterReplicaConfiguration("PRIMARY_HOST", 6379);
  config.addNode("REPLICA_HOST", 6379);
  config.setPassword(RedisPassword.of("YOUR_PASSWORD"));

  LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
      .readFrom(ReadFrom.REPLICA_PREFERRED)
      .build();

  return new LettuceConnectionFactory(config, clientConfig);
}

@Bean
public RedisTemplate<String, Object> primaryRedisTemplate(
  @Qualifier("primaryConnectionFactory") RedisConnectionFactory factory) {

  RedisTemplate<String, Object> template = new RedisTemplate<>();
  template.setConnectionFactory(factory);
  return template;
}

@Bean
public RedisTemplate<String, Object> replicaRedisTemplate(
  @Qualifier("replicaConnectionFactory") RedisConnectionFactory factory) {

  RedisTemplate<String, Object> template = new RedisTemplate<>();
  template.setConnectionFactory(factory);
  return template;
}

Node.js

import { createClient } from 'redis';

async function main() {
// Initialize the primary client connecting only to the primary endpoint
const primaryClient = createClient({
  url: 'redis://PRIMARY_HOST:6379',
  password: 'YOUR_PASSWORD'
});

// Initialize the replica client connecting only to the replica endpoint
const replicaClient = createClient({
  url: 'redis://REPLICA_HOST:6379',
  password: 'YOUR_PASSWORD'
});

primaryClient.on('error', (err) => console.error('Primary Client Error', err));
replicaClient.on('error', (err) => console.error('Replica Client Error', err));

await primaryClient.connect();
await replicaClient.connect();

// Use the primary client for all mutating commands
try {
  await primaryClient.set('example_key', 'example_value');
  console.log('Successfully wrote to primary');
} catch (err) {
  console.error('Failed to write to primary:', err);
}

// Use the replica client for all read-only commands
try {
  const val = await replicaClient.get('example_key');
  console.log(`Successfully read value: ${val}`);
} catch (err) {
  console.error('Failed to read from replica:', err);
}

await primaryClient.disconnect();
await replicaClient.disconnect();
}

main();

Python

import redis

def main():
  # Initialize the primary client connecting only to the primary endpoint
  primary_client = redis.Redis(
      host='PRIMARY_HOST',
      port=6379,
      password='YOUR_PASSWORD',
      decode_responses=True
  )

  # Initialize the replica client connecting only to the replica endpoint
  replica_client = redis.Redis(
      host='REPLICA_HOST',
      port=6379,
      password='YOUR_PASSWORD',
      decode_responses=True
  )

  # Use the primary client for all mutating commands
  try:
      primary_client.set('example_key', 'example_value')
      print('Successfully wrote to primary')
  except redis.RedisError as e:
      print(f'Failed to write to primary: {e}')

  # Use the replica client for all read-only commands
  try:
      val = replica_client.get('example_key')
      print(f'Successfully read value: {val}')
  except redis.RedisError as e:
      print(f'Failed to read from replica: {e}')

if __name__ == '__main__':
  main()