Las siguientes secciones enumeran las opciones de configuración que puede realizar para reducir la latencia en su 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 su aplicación están distribuidos en varias zonas geográficas, como EE. UU. y Europa, le recomendamos implementarla en una o más regiones de cada zona geográfica para reducir la latencia de los usuarios en cada ubicación. Para obtener más información sobre las implementaciones multirregionales, consulte Paradigma de implementación multirregional.
Si sus datos están divididos por geografía, de modo que los usuarios de cada geografía acceden a diferentes conjuntos de datos, también 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
majority, que requiere que cada operación de escritura se replique en la mayoría de los nodos con derecho a voto que contienen datos en su conjunto de réplicas antes de que Atlas la reconozca como completada y exitosa para el cliente. Configurar un nivel de escritura más alto aumenta la latencia de escritura, pero también mejora la durabilidad de la escritura y evita reversiones durante una conmutación por error del conjunto 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 de consultas: Puede establecer límites de tiempo de espera de consultas globales y a nivel de operación en su implementación para reducir el tiempo que su aplicación espera una respuesta antes de agotar el tiempo de espera. Esto puede evitar que las consultas continuas afecten negativamente el rendimiento de la implementación durante largos periodos.
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 Sisu región de AWS esus-east-1(Norte de Virginia) yus-west-1(Norte de California), y la mayoría de sus usuarios se encuentran en California, puede priorizar los nodos en la región de AWSus-west-1(Norte de California) para garantizar que el nodo principal esté siempre geográficamente cerca de la mayoría de sus 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 red
Puede aumentar la seguridad y reducir aún más la latencia utilizando las siguientes opciones de conectividad de red:
nodos privados: Los puntos finales privados establecen conexiones directas y seguras entre la red virtual de su aplicación y su clúster Atlas, lo que reduce potencialmente los saltos de red y mejora 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 utiliza recursos de un solo hilo, como AWS Lambda, tu aplicación solo podrá abrir y usar una conexión de cliente a la vez. Para aprender más acerca de cómo y dónde crear y usar un pool de conexiones, y dónde especificar la configuración del pool de conexiones, consulte Descripción general del pool de conexiones.
Ejemplo de aplicación de baja latencia
La siguiente aplicación de muestra reúne recomendaciones clave de esta página para reducir la latencia de la operación 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 debe agregar a su proyecto como dependencia 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.")