Mejores Prácticas para Optimizar el Rendimiento de Lectura en Conjuntos de Réplicas

Mejora las lecturas en conjuntos de réplicas de MongoDB con preferencia de lectura, preocupación de lectura, monitoreo de retraso, indexación y controles de tiempo de espera.

Mejores Prácticas para Optimizar el Rendimiento de Lectura en Conjuntos de Réplicas

Los conjuntos de réplicas de MongoDB ofrecen alta disponibilidad, pero no hacen que las lecturas sean más rápidas automáticamente. Si tu aplicación envía cada consulta al primario, utiliza índices débiles o lee de secundarios con retraso, los usuarios lo sentirán como páginas lentas y datos obsoletos.

Un buen rendimiento de lectura proviene de elegir la preferencia de lectura correcta, igualar la preocupación de lectura con la consistencia que necesitas, monitorear el retraso de replicación y corregir primero las consultas lentas.

Entendiendo la Ruta de Lectura en Conjuntos de Réplicas

En una implementación estándar de conjunto de réplicas, un miembro se designa como el primario, manejando todas las escrituras. Los miembros restantes son secundarios, que replican asincrónicamente los datos del primario. Las lecturas de la aplicación pueden dirigirse al primario o distribuirse entre los secundarios, dependiendo de la configuración.

Optimizar las lecturas significa equilibrar la necesidad de consistencia inmediata de datos (que a menudo requiere leer del primario) con el deseo de descargar tráfico del primario (leyendo de secundarios).

1. Uso Estratégico de las Preocupaciones de Lectura

Preocupación de Lectura define el grado de consistencia de datos requerido para las operaciones de lectura. Establecer una preocupación de lectura demasiado estricta cuando una más relajada es suficiente es una causa común de latencia de lectura, ya que puede forzar a la operación a esperar confirmaciones de múltiples nodos.

Preocupaciones de Lectura Disponibles

MongoDB ofrece varias preocupaciones de lectura, cada una intercambiando latencia por durabilidad/consistencia:

Preocupación de Lectura Descripción Caso de Uso
majority Devuelve datos confirmados como comprometidos por una mayoría de nodos votantes. Predeterminado estándar. Lecturas de propósito general que requieren alta durabilidad.
local Devuelve los datos más recientes disponibles en el miembro desde el que se lee, independientemente de la confirmación de escritura. Lecturas que pueden tolerar algunos datos obsoletos (por ejemplo, contadores de panel).
linearizable Lee del primario y refleja todas las escrituras confirmadas antes de que comenzara la lectura. Requiere readConcern: "linearizable" y preocupación de escritura mayoritaria para las escrituras relevantes. Lecturas raras que deben observar el último estado confirmado, como verificaciones de propiedad de bloqueo.

Consejo de Optimización: Predeterminar a local o majority

Para lecturas no críticas (como cargar datos de configuración actualizados con poca frecuencia o resultados en caché), usa la preocupación de lectura local en secundarios. Esto evita cualquier retraso de sincronización.

Ejemplo: Establecer Preocupación de Lectura a Nivel de Sesión

// Establecer preocupación de lectura a 'local' para esta sesión específica
const session = mongoClient.startSession({ readConcern: { level: "local" } });

// Operación de búsqueda usando la sesión
db.collection('mydata').find().session(session).toArray();

Advertencia: Leer con preocupación local en un secundario puede devolver datos obsoletos en relación con el primario.

2. Distribuir Lecturas Entre Secundarios

Por defecto, MongoDB dirige las lecturas al primario. Para escalar la capacidad de lectura, debes dirigir explícitamente las lecturas a secundarios usando configuraciones de Preferencia de Lectura.

Entendiendo la Preferencia de Lectura

La Preferencia de Lectura dicta qué miembros del conjunto de réplicas son elegibles para satisfacer solicitudes de lectura y en qué orden deben ser elegidos.

Preferencias de Lectura Comunes incluyen:

  • primary: (Predeterminado) Solo el primario es elegible.
  • primaryPreferred: Intenta primero con el primario; recurre a un secundario si el primario no está disponible.
  • secondary: Solo los secundarios son elegibles. Si no hay secundarios disponibles, la operación falla.
  • secondaryPreferred: Prefiere secundarios; recurre al primario si no hay secundarios disponibles.
  • nearest: Elige el miembro (primario o secundario) con la latencia de red más baja hacia el cliente.

Consejo de Optimización: Usar secondaryPreferred o nearest

Para la mayoría de las aplicaciones con muchas lecturas, usar secondaryPreferred permite distribuir la carga de consultas entre todos los secundarios disponibles, reduciendo significativamente la carga en el primario.

Si tienes servidores de aplicación distribuidos geográficamente, nearest es a menudo la mejor opción, ya que minimiza la latencia de red para el cliente, incluso si ocasionalmente golpea al primario.

Ejemplo: Conectar con secondaryPreferred

Al conectar el controlador de tu aplicación, especifica la preferencia de lectura:

const uri = "mongodb://host1,host2,host3/?replicaSet=rs0&readPreference=secondaryPreferred";
// O usando opciones de conexión en una configuración de controlador
const options = {
  readPreference: "secondaryPreferred"
};

3. Gestionar la Sincronización y el Retraso de Secundarios

Si estás dirigiendo lecturas a secundarios, el rendimiento de esas lecturas depende completamente de qué tan rápido los secundarios se mantienen al día con el primario. Un retraso de replicación alto significa que los secundarios están sirviendo datos obsoletos, o si el retraso es demasiado alto, las lecturas podrían fallar o agotar el tiempo de espera.

Monitorear el Retraso de Replicación

Siempre monitorea la diferencia de optime entre el primario y los secundarios. rs.status() muestra el estado de replicación por miembro, y herramientas administradas como MongoDB Atlas, Cloud Manager u Ops Manager pueden alertar sobre el retraso.

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

Impacto de la Preocupación de Escritura en el Rendimiento del Secundario

Aunque este artículo se centra en lecturas, configuraciones altas de preocupación de escritura pueden impactar indirectamente el rendimiento de lectura al ralentizar el primario, lo que a su vez hace que los secundarios se queden más atrás.

Por ejemplo, requerir w: "majority" significa que el cliente no recibe confirmación hasta que la escritura haya llegado a una mayoría de miembros votantes que contienen datos. Si los secundarios son lentos debido a presión de disco o red, la latencia de escritura de la aplicación puede aumentar, y esos mismos secundarios sobrecargados también pueden servir lecturas lentas.

Mejor Práctica para Preocupación de Escritura (Optimización Indirecta de Lectura): No bajes la preocupación de escritura solo para hacer que las lecturas parezcan más rápidas. Elige la preocupación de escritura basada en los requisitos de durabilidad, luego corrige la causa del retraso: discos lentos, secundarios sobrecargados, oplog subdimensionado, problemas de red o consultas que compiten con la replicación.

4. Indexación y Optimización de Consultas

Ninguna configuración puede superar una consulta mal escrita. El principio fundamental de las lecturas rápidas sigue siendo una indexación robusta.

Consideraciones Clave de Indexación

  1. Consultas Cubiertas: Diseña consultas que puedan ser completamente satisfechas por un índice sin recuperar documentos del disco. Estas son las lecturas más rápidas posibles.
  2. Alineación de Índices: Asegúrate de que los índices coincidan con los campos utilizados en tus cláusulas find(), sort() y projection().
  3. Evitar Escaneos de Colección: Siempre verifica en el perfilador de consultas que las operaciones de lectura estén usando índices (IXSCAN) en lugar de realizar escaneos completos de colección (COLLSCAN).

Ajustar Tiempos de Espera de Consultas

Si una aplicación está golpeando un secundario con mucho retraso, la consulta podría agotar el tiempo de espera. Configura tiempos de espera razonables en tu aplicación para manejar elegantemente el retraso temporal, quizás recurriendo al primario o reintentando más tarde, en lugar de colgarse indefinidamente.

Resumen de Pasos para Optimizar Lecturas

Para lograr un rendimiento de lectura óptimo en tu conjunto de réplicas de MongoDB, sigue estos pasos accionables:

  1. Identificar Tipos de Lectura: Clasifica las lecturas en aquellas que necesitan datos frescos del primario y aquellas que pueden tolerar consistencia eventual de secundarios.
  2. Configurar Preferencia de Lectura: Establece la cadena de conexión o las opciones de sesión para usar secondaryPreferred o nearest para la mayoría del tráfico de la aplicación.
  3. Monitorear Retraso: Monitorea continuamente el retraso de replicación desde rs.status(), métricas del controlador o tu plataforma de monitoreo. Si el retraso es consistentemente alto, investiga problemas de hardware secundario o de red.
  4. Revisar Preocupaciones de Escritura: Asegúrate de que las preocupaciones de escritura no estén ralentizando indebidamente al primario, lo que priva a los secundarios de datos frescos.
  5. Indexar a Fondo: Verifica que todas las rutas de lectura ejecutadas con frecuencia utilicen índices eficientes.

El escalado de lectura en conjuntos de réplicas funciona mejor cuando eres honesto sobre la obsolescencia. Envía lecturas críticas para el usuario al primario cuando deben estar actualizadas, usa secundarios para análisis o paneles que puedan tener retraso, y sigue midiendo los planes de consulta y la salud de la replicación a medida que el tráfico cambia.