Melhores Práticas para Otimizar o Desempenho de Leitura em Conjuntos de Réplicas

Melhore as leituras de conjuntos de réplicas do MongoDB com preferência de leitura, preocupação de leitura, monitoramento de lag, indexação e controles de tempo limite.

Melhores Práticas para Otimizar o Desempenho de Leitura em Conjuntos de Réplicas

Os conjuntos de réplicas do MongoDB oferecem alta disponibilidade, mas não tornam automaticamente as leituras mais rápidas. Se sua aplicação envia toda consulta para o primário, usa índices fracos ou lê de secundários com lag, os usuários sentirão isso como páginas lentas e dados desatualizados.

Um bom desempenho de leitura vem da escolha da preferência de leitura correta, combinando a preocupação de leitura com a consistência necessária, monitorando o lag de replicação e corrigindo consultas lentas primeiro.

Entendendo o Caminho de Leitura em Conjuntos de Réplicas

Em uma implantação padrão de conjunto de réplicas, um membro é designado como primário, lidando com todas as escritas. Os membros restantes são secundários, que replicam assincronamente os dados do primário. As leituras da aplicação podem ser direcionadas ao primário ou distribuídas entre os secundários, dependendo da configuração.

Otimizar leituras significa equilibrar a necessidade de consistência imediata dos dados (que muitas vezes exige leitura do primário) com o desejo de aliviar o tráfego do primário (lendo de secundários).

1. Uso Estratégico das Preocupações de Leitura

Preocupação de Leitura define o grau de consistência dos dados necessário para operações de leitura. Definir uma preocupação de leitura excessivamente rigorosa quando uma mais relaxada é suficiente é uma causa comum de latência de leitura, pois pode forçar a operação a esperar por confirmações de vários nós.

Preocupações de Leitura Disponíveis

O MongoDB oferece várias preocupações de leitura, cada uma trocando latência por durabilidade/consistência:

Preocupação de Leitura Descrição Caso de Uso
majority Retorna dados reconhecidos como confirmados por uma maioria de nós votantes. Padrão padrão. Leituras de uso geral que exigem alta durabilidade.
local Retorna os dados mais recentes disponíveis no membro que está sendo lido, independentemente da confirmação de escrita. Leituras que podem tolerar alguns dados desatualizados (ex: contadores de painel).
linearizable Lê do primário e reflete todas as escritas reconhecidas antes do início da leitura. Requer readConcern: "linearizable" e preocupação de escrita majoritária para as escritas relevantes. Leituras raras que devem observar o estado confirmado mais recente, como verificações de propriedade de bloqueio.

Dica de Otimização: Padrão para local ou majority

Para leituras não críticas (como carregar dados de configuração atualizados com pouca frequência ou resultados em cache), use a preocupação de leitura local nos secundários. Isso evita qualquer atraso de sincronização.

Exemplo: Definindo Preocupação de Leitura no Nível da Sessão

// Define a preocupação de leitura como 'local' para esta sessão específica
const session = mongoClient.startSession({ readConcern: { level: "local" } });

// Operação de busca usando a sessão
db.collection('mydata').find().session(session).toArray();

Aviso: Ler com preocupação local em um secundário pode retornar dados desatualizados em relação ao primário.

2. Distribuindo Leituras Entre Secundários

Por padrão, o MongoDB direciona as leituras para o primário. Para escalar a capacidade de leitura, você deve explicitamente direcionar leituras para secundários usando as configurações de Preferência de Leitura.

Entendendo a Preferência de Leitura

A Preferência de Leitura determina quais membros do conjunto de réplicas são elegíveis para atender solicitações de leitura e em que ordem devem ser escolhidos.

Preferências de Leitura Comuns incluem:

  • primary: (Padrão) Apenas o primário é elegível.
  • primaryPreferred: Tenta o primário primeiro; cai para um secundário se o primário estiver indisponível.
  • secondary: Apenas secundários são elegíveis. Se nenhum secundário estiver disponível, a operação falha.
  • secondaryPreferred: Prefere secundários; cai para o primário se nenhum secundário estiver disponível.
  • nearest: Escolhe o membro (primário ou secundário) com a menor latência de rede para o cliente.

Dica de Otimização: Usando secondaryPreferred ou nearest

Para a maioria das aplicações com uso intenso de leitura, usar secondaryPreferred permite distribuir a carga de consultas por todos os secundários disponíveis, reduzindo significativamente a carga no primário.

Se você tem servidores de aplicação geograficamente distribuídos, nearest é frequentemente a melhor escolha, pois minimiza a latência de rede para o cliente, mesmo que ocasionalmente atinja o primário.

Exemplo: Conectando com secondaryPreferred

Ao conectar o driver da sua aplicação, especifique a preferência de leitura:

const uri = "mongodb://host1,host2,host3/?replicaSet=rs0&readPreference=secondaryPreferred";
// Ou usando opções de conexão na configuração do driver
const options = {
  readPreference: "secondaryPreferred"
};

3. Gerenciando Sincronização e Lag do Secundário

Se você está roteando leituras para secundários, o desempenho dessas leituras depende inteiramente de quão rápido os secundários estão acompanhando o primário. Lag de replicação alto significa que os secundários estão servindo dados desatualizados, ou se o lag for muito alto, as leituras podem falhar ou expirar.

Monitorando o Lag de Replicação

Sempre monitore a diferença de optime entre o primário e os secundários. rs.status() mostra o estado de replicação por membro, e ferramentas gerenciadas como MongoDB Atlas, Cloud Manager ou Ops Manager podem alertar sobre lag.

rs.status().members.map(m => ({
  name: m.name,
  stateStr: m.stateStr,
  optimeDate: m.optimeDate
}))

Impacto da Preocupação de Escrita no Desempenho do Secundário

Embora este artigo foque em leituras, configurações altas de preocupação de escrita podem impactar indiretamente o desempenho de leitura ao desacelerar o primário, o que por sua vez faz com que os secundários fiquem ainda mais para trás.

Por exemplo, exigir w: "majority" significa que o cliente não recebe confirmação até que a escrita tenha alcançado uma maioria dos membros votantes que contêm dados. Se os secundários estão lentos devido à pressão de disco ou rede, a latência de escrita da aplicação pode aumentar, e esses mesmos secundários sobrecarregados podem também servir leituras lentas.

Melhor Prática para Preocupação de Escrita (Otimização Indireta de Leitura): Não reduza a preocupação de escrita apenas para tornar as leituras mais rápidas. Escolha a preocupação de escrita com base nos requisitos de durabilidade, depois corrija a causa do lag: discos lentos, secundários sobrecarregados, oplog subdimensionado, problemas de rede ou consultas competindo com a replicação.

4. Indexação e Otimização de Consultas

Nenhuma configuração pode superar uma consulta mal escrita. O princípio fundamental de leituras rápidas continua sendo uma indexação robusta.

Considerações Chave sobre Indexação

  1. Consultas Cobertas: Projete consultas que podem ser totalmente satisfeitas por um índice sem buscar documentos do disco. Estas são as leituras mais rápidas possíveis.
  2. Alinhamento de Índices: Garanta que os índices correspondam aos campos usados em suas cláusulas find(), sort() e projection().
  3. Evite Varreduras de Coleção: Sempre verifique no profiler de consultas que as operações de leitura estão usando índices (IXSCAN) em vez de realizar varreduras completas de coleção (COLLSCAN).

Ajustando Timeouts de Consulta

Se uma aplicação está atingindo um secundário com muito lag, a consulta pode expirar. Configure timeouts razoáveis em sua aplicação para lidar graciosamente com lag temporário, talvez caindo para o primário ou tentando novamente mais tarde, em vez de travar indefinidamente.

Resumo das Etapas de Otimização de Leitura

Para alcançar o desempenho ideal de leitura em seu conjunto de réplicas do MongoDB, siga estas etapas acionáveis:

  1. Identifique Tipos de Leitura: Classifique as leituras entre aquelas que precisam de dados frescos do primário e aquelas que podem tolerar consistência eventual dos secundários.
  2. Configure a Preferência de Leitura: Defina a string de conexão ou as opções de sessão para usar secondaryPreferred ou nearest para a maioria do tráfego da aplicação.
  3. Monitore o Lag: Monitore continuamente o lag de replicação a partir de rs.status(), métricas do driver ou sua plataforma de monitoramento. Se o lag for consistentemente alto, investigue problemas de hardware ou rede dos secundários.
  4. Revise as Preocupações de Escrita: Garanta que as preocupações de escrita não estejam desacelerando indevidamente o primário, o que priva os secundários de dados frescos.
  5. Indexe Completamente: Verifique se todos os caminhos de leitura executados com frequência usam índices eficientes.

O escalonamento de leitura de conjuntos de réplicas funciona melhor quando você é honesto sobre a desatualização. Envie leituras críticas do usuário para o primário quando elas devem estar atualizadas, use secundários para análises ou painéis que podem ter lag, e continue medindo planos de consulta e saúde da replicação à medida que o tráfego muda.