Las siguientes secciones enumeran opciones de configuración que puedes ajustar para reducir la latencia en tu implementación de Atlas.
Distancia física
La distancia física es la principal causa de latencia. La distancia entre los usuarios y tu aplicación, entre tu aplicación y tus datos, y entre los nodos del clúster, impacta en la latencia del sistema y el rendimiento de la aplicación.
Para reducir la latencia en las operaciones de lectura y guardar, es crucial ubicar tanto tu aplicación como tus datos geográficamente más cerca de los usuarios. Los nodos Atlas que contienen datos son nodos de servidor dentro de un clúster de Atlas que almacenan los datos de su base de datos y gestionan las operaciones de lectura y escritura. Para respaldar un acceso a los datos más rápido para los usuarios de tu aplicación, implementa nodos de Atlas con datos en regiones del proveedor de nube que estén geográficamente cerca de la mayoría de los usuarios de tu aplicación.
Si los usuarios de tu aplicación están distribuidos en varias áreas geográficas, por ejemplo, entre EE.CC. Y Europa, te recomendamos implementar una o más regiones en cada área para reducir la latencia de los usuarios en cada ubicación. Para obtener más información sobre las implementaciones en multiregión, consulta Paradigma de implementación multiregión.
Si sus datos están divididos por geografía, de modo que los usuarios en cada geografía acceden a diferentes conjuntos de datos, también se puede partición tus datos por región o ubicación geográfica para optimizar el rendimiento de lectura y escritura para los usuarios en cada ubicación geográfica. Este enfoque te permite gestionar grandes conjuntos de datos y un alto rendimiento, garantizando al mismo tiempo la localidad de datos.
Configuración de replicación
Replicación es la copia de datos desde el nodo primario a los nodos secundarios. Las siguientes opciones de configuración de replicación pueden ajustarse para minimizar la latencia de las operaciones de lectura y escritura en un set de réplicas:
Niveles de nivel de confirmación de escritura (write concern): Existe una compensación entre la latencia de escritura y la durabilidad de escritura cuando se configura el nivel de confirmación de escritura (write concern). El nivel de confirmación de escritura (write concern) global por defecto de MongoDB se establece en
majoritylo que requiere que cada operación de escritura se replique en la mayoría de los nodos con capacidad de voto y de datos en tu set de réplicas antes de que Atlas reconozca la operación como completa y exitosa para el cliente. Establecer un nivel de confirmación de escritura (write concern) más alto aumenta la latencia de escritura, pero también mejora la durabilidad de la escritura y previene rollbacks durante una conmutación por error en un set de réplicas.Nivel de consistencia de lectura y Preferencia de lectura: Hay una compensación entre la latencia de consulta, la disponibilidad de datos y la coherencia de las respuestas de las consultas al configurar nivel de consistencia de lectura y preferencia de lectura. El nivel de consistencia de lectura global por defecto de MongoDB es
local, que requiere que las operaciones de lectura se realicen únicamente desde un nodo del set de réplicas local sin esperar la confirmación de que los datos se hayan replicado en otros nodos. Si este nodo es el principal o uno secundario se determina según tu preferencia de lectura, que Atlas configura enprimarypor defecto. Esta combinación **por defecto** de **nivel de consistencia de lectura** y preferencia se optimiza para las lecturas de menor **latencia** desde el **nodo** más actualizado en el **set de réplicas**, pero también conlleva el riesgo de que los datos del **nodo primario** puedan no ser **durables** y potencialmente pueden revertirse durante un cambio de rol, y de que los usuarios no puedan consultar datos si no hay un **nodo primario** disponible.Puedes cambiar tu preferencia de lectura a
primaryPreferredpara permitir que las operaciones de lectura se realicen desde un nodo secundario cuando no haya un nodo primario disponible, o si hay un nodo secundario geográficamente más cercano, pero esto tiene el riesgo de devolver datos obsoletos si el nodo secundario no está actualizado. Este riesgo se puede mitigar aumentando su nivel de confirmación de escritura (write concern) para garantizar que más nodos secundarios estén actualizados, con la desventaja de que esto aumentará su latencia de escritura.Importante
Tenga en cuenta que existe la posibilidad de que un nodo secundario devuelva datos obsoletos debido al retraso de replicación.
Límites de tiempo de espera para query: Puedes establecer límites de tiempo de espera para query a nivel global y de operación en tu implementación para reducir el tiempo que tu aplicación espera una respuesta antes de que se agote el tiempo de espera. Esto puede evitar que las consultas continuas afecten negativamente al rendimiento de la implementación durante largos periodos de tiempo.
Prioridad de Elección del Nodo: Para aumentar la probabilidad de que los nodos de tu centro de datos principal sean elegidos como primarios antes que los nodos de un centro de datos alterno durante una elección de set de réplicas, puedes establecer el
members[n].priorityde los nodos en el centro de datos alterno para que sea inferior al de los nodos en el centro de datos principal. Por ejemplo, si implementa su clúster en AWS regionesus-east-1(Virginia del Norte) yus-west-1(California del Norte), y la mayoría de tus usuarios están en California, puedes dar prioridad a los nodos en la región AWSus-west-1(California del Norte) para asegurar que el nodo principal esté siempre geográficamente cercano a la mayoría de tus usuarios y pueda responder a las operaciones de lectura y escritura con una latencia mínima.Lecturas espejeadas: Las lecturas espejeadas reducen el impacto de las elecciones primarias tras una Interrupción del servicio al calentar previamente las cachés en los nodos secundarios. Para obtener más información, consulta lecturas espejeadas.
Para obtener más orientación sobre cómo implementar la mejor configuración de replicación para tus necesidades, comunícate con los Professional Services de MongoDB.
Configuración de la red
Puedes incrementar la seguridad y reducir aún más la latencia usando las siguientes opciones de conectividad de red:
nodos privados: Los nodos privados establecen conexiones directas y seguras entre la red virtual de tu aplicación y tu clúster de Atlas, lo que puede reducir los saltos de red y mejorar la latencia.
VPC emparejamiento: Configura el VPC emparejamiento en tus sets de réplicas para permitir que las aplicaciones se conecten a las regiones asociadas. En caso de un failover, el VPC emparejamiento ofrece una forma para que un servidor de aplicaciones detecte un nuevo nodo primario y enrute el tráfico en consecuencia.
Modelado de datos y optimización de consultas
La velocidad a la que tu aplicación accede a los datos contribuye a la latencia. Un buen modelado de datos y una optimización de query pueden mejorar la velocidad de acceso a los datos. Por ejemplo, se puede:
Reducir el tamaño del documento: Considera acortar los nombres de campos y las longitudes de los valores para disminuir la cantidad de transferencia de datos a través de la red.
Optimice los patrones de query: Utilice los índices de manera eficaz para minimizar la cantidad de datos que deben leerse entre regiones.
Supervisión y prueba de latencia
Atlas proporciona el Panel de Rendimiento en Tiempo Real (RTPP) para observar métricas de latencia en diferentes regiones. También puedes implementar supervisión a nivel de aplicación para rastrear la latencia de extremo a extremo hacia y desde la aplicación. Antes de la implementación final en producción, recomendamos realizar pruebas de rendimiento bajo los diversos escenarios multiregión para identificar y abordar los cuellos de botella de latencia.
Para obtener más información sobre cómo supervisar tu implementación, consulta Orientación para la supervisión y alertas de Atlas.
Configuración de conexión
Recomendamos que utilices un método de conexión basado en la versión más actual del controlador para el lenguaje de programación de tu aplicación siempre que sea posible. Aunque la cadena de conexión por defecto que Atlas proporciona es un buen punto de partida, puedes agregar opciones de cadenas de conexión a tu cadena de conexión para mejorar el rendimiento en el contexto de tu aplicación específica y arquitectura de implementación.
Para implementaciones de aplicaciones a nivel empresarial, es especialmente importante ajustar la configuración del pool de conexiones para satisfacer la demanda de los usuarios y al mismo tiempo minimizar la latencia operativa. Por ejemplo, puedes utilizar las opciones minPoolSize y maxPoolSize para ajustar cómo y cuándo se abren la mayoría de las conexiones de clientes de tu base de datos, lo que te permite prevenir o planificar los picos de latencia que vienen con la sobrecarga de red asociada.
El grado en que puedes configurar estas opciones depende de tu arquitectura de implementación. Por ejemplo, si la implementación de tu aplicación está aprovechando recursos de un solo subproceso, como AWS Lambda, tu aplicación solo podrá abrir y utilizar una conexión de cliente. Para obtener más información sobre cómo y dónde crear y utilizar un pool de conexiones, y dónde especificar la configuración del pool de conexiones, consulta Descripción general del pool de conexiones.
Ejemplo de aplicación de baja latencia
La siguiente aplicación de muestra reúne recomendaciones clave en esta página para reducir la latencia de las operaciones de datos:
Utilice la cadena de conexión proporcionada por Atlas con escrituras reintentables, nivel de confirmación de escritura de mayoría y nivel de consistencia de lectura por defecto.
Especificar un límite de operation time con el método maxTimeMS. Para obtener instrucciones sobre cómo configurar
maxTimeMS, consulta tu documentación del driver.Gestione errores por claves duplicadas y "timeouts".
La aplicación es una API HTTP que permite a los clientes crear o listar registros de usuario. Expone un endpoint que acepta solicitudes GET y POST http://localhost:3000:
Método | Endpoint | Descripción |
|---|---|---|
|
| Obtiene una lista de nombres de usuario de una colección |
|
| Requiere un |
Nota
1 // File: App.java 2 3 import java.util.Map; 4 import java.util.logging.Logger; 5 6 import org.bson.Document; 7 import org.json.JSONArray; 8 9 import com.mongodb.MongoException; 10 import com.mongodb.client.MongoClient; 11 import com.mongodb.client.MongoClients; 12 import com.mongodb.client.MongoCollection; 13 import com.mongodb.client.MongoDatabase; 14 15 import fi.iki.elonen.NanoHTTPD; 16 17 public class App extends NanoHTTPD { 18 private static final Logger LOGGER = Logger.getLogger(App.class.getName()); 19 20 static int port = 3000; 21 static MongoClient client = null; 22 23 public App() throws Exception { 24 super(port); 25 26 // Replace the uri string with your MongoDB deployment's connection string 27 String uri = "<atlas-connection-string>"; 28 client = MongoClients.create(uri); 29 30 start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); 31 LOGGER.info("\nStarted the server: http://localhost:" + port + "/ \n"); 32 } 33 34 public static void main(String[] args) { 35 try { 36 new App(); 37 } catch (Exception e) { 38 LOGGER.severe("Couldn't start server:\n" + e); 39 } 40 } 41 42 43 public Response serve(IHTTPSession session) { 44 StringBuilder msg = new StringBuilder(); 45 Map<String, String> params = session.getParms(); 46 47 Method reqMethod = session.getMethod(); 48 String uri = session.getUri(); 49 50 if (Method.GET == reqMethod) { 51 if (uri.equals("/")) { 52 msg.append("Welcome to my API!"); 53 } else if (uri.equals("/users")) { 54 msg.append(listUsers(client)); 55 } else { 56 msg.append("Unrecognized URI: ").append(uri); 57 } 58 } else if (Method.POST == reqMethod) { 59 try { 60 String name = params.get("name"); 61 if (name == null) { 62 throw new Exception("Unable to process POST request: 'name' parameter required"); 63 } else { 64 insertUser(client, name); 65 msg.append("User successfully added!"); 66 } 67 } catch (Exception e) { 68 msg.append(e); 69 } 70 } 71 72 return newFixedLengthResponse(msg.toString()); 73 } 74 75 static String listUsers(MongoClient client) { 76 MongoDatabase database = client.getDatabase("test"); 77 MongoCollection<Document> collection = database.getCollection("users"); 78 79 final JSONArray jsonResults = new JSONArray(); 80 collection.find().forEach((result) -> jsonResults.put(result.toJson())); 81 82 return jsonResults.toString(); 83 } 84 85 static String insertUser(MongoClient client, String name) throws MongoException { 86 MongoDatabase database = client.getDatabase("test"); 87 MongoCollection<Document> collection = database.getCollection("users"); 88 89 collection.insertOne(new Document().append("name", name)); 90 return "Successfully inserted user: " + name; 91 } 92 }
Nota
La siguiente aplicación de servidor utiliza Express, que debes agregar como una dependencia a tu proyecto antes de poder ejecutarlo.
1 const express = require('express'); 2 const bodyParser = require('body-parser'); 3 4 // Use the latest client libraries by installing & importing them 5 const MongoClient = require('mongodb').MongoClient; 6 7 const app = express(); 8 app.use(bodyParser.json()); 9 app.use(bodyParser.urlencoded({ extended: true })); 10 11 const uri = "mongodb+srv://<db_username>:<db_password>@cluster0-111xx.mongodb.net/test?retryWrites=true&w=majority"; 12 13 const client = new MongoClient(uri, { 14 useNewUrlParser: true, 15 useUnifiedTopology: true 16 }); 17 18 // ----- API routes ----- // 19 app.get('/', (req, res) => res.send('Welcome to my API!')); 20 21 app.get('/users', (req, res) => { 22 const collection = client.db("test").collection("users"); 23 24 collection 25 .find({}) 26 .maxTimeMS(5000) 27 .toArray((err, data) => { 28 if (err) { 29 res.send("The request has timed out. Please check your connection and try again."); 30 } 31 return res.json(data); 32 }); 33 }); 34 35 app.post('/users', (req, res) => { 36 const collection = client.db("test").collection("users"); 37 collection.insertOne({ name: req.body.name }) 38 .then(result => { 39 res.send("User successfully added!"); 40 }, err => { 41 res.send("An application error has occurred. Please try again."); 42 }) 43 }); 44 // ----- End of API routes ----- // 45 46 app.listen(3000, () => { 47 console.log(`Listening on port 3000.`); 48 client.connect(err => { 49 if (err) { 50 console.log("Not connected: ", err); 51 process.exit(0); 52 } 53 console.log('Connected.'); 54 }); 55 });
Nota
La siguiente aplicación web utiliza FastAPI. Para crear una nueva aplicación, se debe usar la estructura del archivo de muestra de FastAPI.
1 # File: main.py 2 3 from fastapi import FastAPI, Body, Request, Response, HTTPException, status 4 from fastapi.encoders import jsonable_encoder 5 6 from typing import List 7 from models import User 8 9 import pymongo 10 from pymongo import MongoClient 11 from pymongo import errors 12 13 # Replace the uri string with your |service| connection string 14 uri = "<atlas-connection-string>" 15 db = "test" 16 17 app = FastAPI() 18 19 20 def startup_db_client(): 21 app.mongodb_client = MongoClient(uri) 22 app.database = app.mongodb_client[db] 23 24 25 def shutdown_db_client(): 26 app.mongodb_client.close() 27 28 ##### API ROUTES ##### 29 30 def list_users(request: Request): 31 try: 32 users = list(request.app.database["users"].find().max_time_ms(5000)) 33 return users 34 except pymongo.errors.ExecutionTimeout: 35 raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="The request has timed out. Please check your connection and try again.") 36 37 38 def new_user(request: Request, user: User = Body(...)): 39 user = jsonable_encoder(user) 40 try: 41 new_user = request.app.database["users"].insert_one(user) 42 return {"message":"User successfully added!"} 43 except pymongo.errors.DuplicateKeyError: 44 raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Could not create user due to existing '_id' value in the collection. Try again with a different '_id' value.")