다음 섹션에서는 Atlas 배포에서 지연 시간을 단축하기 위해 선택할 수 있는 구성 옵션을 설명합니다.
물리적 거리
물리적 거리는 지연 시간의 주요 원인입니다. 사용자와 애플리케이션 간 거리, 애플리케이션과 데이터 간 거리, 클러스터 노드 간 거리는 모두 시스템 지연 시간과 애플리케이션 성능에 영향을 미칩니다.
읽기 및 쓰기 (write) 작업의 지연 시간 줄이려면 애플리케이션 과 데이터를 지리적으로 사용자에게 더 가깝게 배치하는 것이 중요합니다. 데이터 보유 Atlas 노드는 데이터베이스의 데이터를 저장하고 읽기 및 쓰기 (write) 작업을 처리하는 Atlas 클러스터 내의 서버 노드입니다. 애플리케이션 사용자의 더 빠른 데이터 액세스 지원 하려면 데이터를 포함하는 Atlas 노드를 대다수의 애플리케이션 사용자와 지리적으로 가까운 cloud 제공자 리전에 배포 .
애플리케이션 사용자가 미국과 유럽 등 여러 지역에 분산되어 있는 경우, 각 지역마다 하나 이상의 리전에 배포해 각 위치에서 사용자의 지연 시간을 줄이는 것이 좋습니다. 멀티 리전 배포서버에 대해 자세히 알아보려면멀티 리전 배포서버 패러다임을 참조하세요.
데이터가 지역별로 나누어져 각 지역의 사용자가 서로 다른 데이터 세트에 액세스 경우 각 지역의 사용자의 읽기 및 쓰기 (write) 성능을 최적화하기 위해 리전 또는 지역별로 데이터를 샤드 할 수도 있습니다. 이 접근 방식을 사용하면 데이터 지역성을 보장하면서 대규모 데이터 세트와 높은 처리량 처리하다 할 수 있습니다.
복제 구성
복제란 프라이머리 노드에서 세컨더리 노드로 데이터를 복사하는 것입니다. 다음 복제 구성 옵션을 조정하여 복제본 세트에서 읽기 및 쓰기 작업의 지연 시간을 최소화할 수 있습니다.
쓰기 고려 (write concern) 수준: 쓰기 고려 (write concern)를 구성할 때 쓰기 (write) 지연 시간과 쓰기 (write) 내구성 간에는 장단점이 있습니다. MongoDB의 기본값 전역 쓰기 고려 (write concern)
majority로 설정하다 있으며, 각 쓰기 (write) 작업은 Atlas 작업이 완료되고 성공적인 것으로 클라이언트 하기 전에 복제본 세트 의 데이터 보유 투표 노드 과반수에 복제되어야 합니다. 쓰기 고려 (write concern)를 높게 설정하면 쓰기 (write) 지연 시간이 늘어나지만 쓰기 (write) 내구성이 향상되고 복제본 세트 페일오버 중 롤백 이 방지됩니다.읽기 고려 (read concern) 및 읽기 설정 (read preference): 읽기 고려 (read concern) 및 읽기 설정 (read preference) 구성할 때 쿼리 지연 시간, 데이터 가용성, 쿼리 응답의 일관성 간에는 장단점이 있습니다. MongoDB의 기본값 글로벌 읽기 고려 (read concern)
local이며, 읽기 작업은 데이터가 다른 노드 간에 복제되는지 확인하기 위해 기다리지 않고 로컬 복제본 세트 의 하나의 노드 에서만 읽습니다. 이 노드 가 프라이머리 머리 노드인지 세컨더리 노드 인지는 Atlas 기본값 으로primary(으)로 설정하는 읽기 설정 (read preference) 에 따라 결정됩니다. 이 기본값 읽기 고려 (read concern) 와 기본 설정 조합은 복제본 세트 의 최신 노드 에서 지연 시간 이 가장 짧은 읽기에 최적화되지만, 프라이머리 노드의 데이터가 지속형 없고 작업이 진행되는 동안 잠재적으로 롤백될 수 있는 위험도 있습니다. 페일오버 가능한 프라이머리 노드 없는 경우 사용자는 데이터를 쿼리 할 수 없습니다.읽기 설정을
primaryPreferred로 변경하면 사용 가능한 프라이머리 노드가 없거나 지리적으로 더 가까운 세컨더리 노드가 있을 때 읽기 작업이 세컨더리 노드에서 수행될 수 있습니다. 그러나 세컨더리 노드가 최신 상태가 아닐 경우 오래된 데이터를 반환할 위험이 있습니다. 쓰기 고려를 증가시켜 더 많은 세컨더리 노드를 최신 상태로 유지함으로써 이러한 위험을 완화할 수 있지만, 이 경우 쓰기 지연 시간이 증가하는 단점이 있습니다.중요
복제 지연으로 인해 세컨더리 노드가 오래된 데이터를 반환할 가능성이 있다는 점을 염두에 두시기 바랍니다.
쿼리 시간 제한: 배포서버에 글로벌 및 작업 수준 쿼리 시간 제한 을 설정하다 애플리케이션 시간이 초과되기 전에 응답을 기다리는 시간을 줄일 수 있습니다. 이렇게 하면 진행 중인 쿼리가 장기간 배포서버 성능에 부정적인 영향이 미치는 것을 방지할 수 있습니다.
노드 투표 우선 순위: 복제본 세트 투표 동안 기본 데이터 센터의 멤버가 대체 데이터 센터의 멤버보다 먼저 프라이머리로 선출되도록 하려면 대체 데이터 센터의 멤버의
members[n].priority를 프라이머리 데이터 센터의 멤버보다 낮게 설정할 수 있습니다. 예를 들어, AWSus-east-1(버지니아 북부) 리전과us-west-1(캘리포니아 북부) 리전에 클러스터를 배포하고, 사용자의 과반수가 캘리포니아에 있는 경우, AWSus-west-1(캘리포니아 북부) 리전의 노드를 우선적으로 배치하여 프라이머리 노드를 항상 과반수의 사용자와 지리적으로 가까운 곳에 위치시키고, 최소한의 지연 시간으로 읽기 및 쓰기 작업에 응답할 수 있도록 합니다.미러링된 읽기: 미러링된 읽기는 장애 후 프라이머리 투표의 영향을 줄이기 위해 세컨더리 노드의 캐시를 사전 예열합니다. 자세한 내용은 미러링된 읽기를 참조하세요.
요구 사항에 가장 적합한 복제 구성을 구현하는 방법에 대한 지침을 알아보려면 MongoDB의 Professional Services에 문의하세요.
네트워크 구성
다음 네트워크 연결 옵션을 사용하여 보안을 강화하고 지연 시간을 추가로 줄일 수 있습니다.
데이터 모델링 및 쿼리 최적화
애플리케이션이 데이터에 액세스하는 속도가 지연 시간에 영향을 미칩니다. 데이터 모델링과 쿼리 최적화를 잘 수행하면 데이터 액세스 속도를 개선할 수 있습니다. 예를 들어 다음과 같은 작업을 수행할 수 있습니다.
문서 크기 줄이기: 네트워크를 통한 데이터 전송량을 줄이기 위해 필드 이름과 값 길이를 줄이는 것이 좋습니다.
쿼리 패턴 최적화: 인덱스를 효과적으로 사용하여 여러 리전에서 읽어야 하는 데이터의 양을 최소화합니다.
지연 시간 모니터링 및 테스트
Atlas 다양한 리전의 지연 시간 지표 관찰할 수 있는 실시간 성능 패널 (실시간 성능 패널) 을 제공합니다. 애플리케이션 수준 모니터링 구현 애플리케이션 과 주고받는 엔드 투 엔드 지연 시간 추적 수도 있습니다. 최종 프로덕션 배포서버 전에 지연 시간 병목 현상을 식별하고 주소 위해 다양한 멀티 리전 시나리오 에서 성능 테스트를 수행하는 것이 좋습니다.
배포서버 모니터링 에 대해 자세히 학습하려면 Atlas 모니터링 및 경고에 대한 지침을 참조하세요.
연결 구성
가능한 경우 애플리케이션의 프로그래밍 언어에 가장 적합한 최신 드라이버 버전으로 빌드된 연결 방법을 사용하는 것이 좋습니다. Atlas가 제공하는 기본값 연결 문자열로 시작하는 것이 가능하지만, 연결 문자열에 연결 문자열 옵션을 추가하여 특정 애플리케이션 및 배포서버 아키텍처의 컨텍스트에서 성능을 개선할 수 있습니다.
엔터프라이즈 수준 애플리케이션 배포서버의 경우 운영 지연 시간 최소화하면서 사용자 수요를 충족하도록 연결 풀 설정을 조정하는 것이 특히 중요합니다. 예시 를 들어 minPoolSize 및 maxPoolSize 옵션을 사용하여 과반수 데이터베이스 클라이언트 연결이 열리는 방법과 시기를 조정하여 관련 네트워크 오버헤드 와 함께 발생하는 지연 시간 급증을 방지하거나 계획할 수 있습니다.
이러한 설정을 구성할 수 있는 범위는 배포서버 아키텍처에 따라 다릅니다. 예시 들어, 애플리케이션 배포서버 AWS Lambda 와 같은 단일 스레드 리소스를 활용하는 경우, 애플리케이션 하나의 클라이언트 연결만 열고 사용할 수 있습니다. 연결 풀 만들고 사용하는 방법과 위치, 연결 풀 설정을 지정하는 위치에 대한 자세한 학습 은 연결 풀 개요를 참조하세요.
지연 시간이 짧은 애플리케이션 예시
다음 샘플 애플리케이션 데이터 작업 지연 시간 줄이기 위해 이 페이지에 주요 권장 사항을 제공합니다.
재시도 가능한 쓰기, 대다수 쓰기 고려 및 기본 읽기 고려와 함께 Atlas에서 제공하는 연결 문자열을 사용합니다.
maxTimeMS 메서드를 사용하여 optime 제한을 지정합니다.
maxTimeMS설정 방법에 대한 지침은 해당 드라이버 설명서를 참조하세요.키 중복 및 시간 초과에 대한 오류를 처리합니다.
이 애플리케이션 클라이언트가 사용자 레코드를 생성하거나 나열할 수 있는 HTTP API 입니다. GET 및 게시 요청을 수락하는 엔드포인트 http://localhost:: 를 노출합니다.3000
메서드 | 엔드포인트 | 설명 |
|---|---|---|
|
|
|
|
| 요청 본문에 |
참고
다음 서버 애플리케이션 프로젝트를 실행 하기 전에 프로젝트 에 종속성으로 추가해야 하는 NanoHTTPD 및 JSON 사용합니다.
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 }
참고
다음 서버 애플리케이션은 Express를 사용하며, 이를 실행하려면 프로젝트에 종속성으로 추가해야 합니다.
1 const express = require('express'); 2 const bodyParser = require('body-parser'); 3 4 // Use the latest drivers 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 });
참고
다음 웹 애플리케이션 FastAPI를 사용합니다. 새 애플리케이션 만들려면 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.")