Azure Cosmos DB .NET SDK에 대한 모범 사례

적용 대상: NoSQL

이 문서에서는 Azure Cosmos DB .NET SDK 사용을 위한 모범 사례를 살펴봅니다. 이러한 연습을 수행하면 대기 시간, 가용성 및 전반적인 성능을 향상시킬 수 있습니다.

Azure Cosmos DB 엔지니어가 .NET SDK를 사용하는 방법에 대한 자세한 내용을 보려면 아래 비디오를 시청하세요!

검사 목록

선택 Subject 세부 정보/링크
SDK 버전 항상 최신 버전의 Azure Cosmos DB SDK를 사용하여 최적의 성능을 제공합니다.
싱글톤 클라이언트 애플리케이션의 수명에 대한 CosmosClient단일 인스턴스를 사용하여 성능을 향상합니다.
지역 대기 시간을 줄이기 위해 가능한 경우, Azure Cosmos DB 계정과 동일한 Azure 지역에서 애플리케이션을 실행해야 합니다. 2~4개의 지역을 사용하도록 설정하고 최상의 가용성을 위해 여러 지역에서 계정을 복제합니다. 프로덕션 작업의 경우 서비스 관리형 장애 조치(failover)를 사용하도록 설정합니다. 이 구성이 없는 경우, 지역 연결이 부족하여 수동 장애 조치(failover)가 성공하지 않으므로 계정의 쓰기 지역 가동 중단 시간에 대한 쓰기 가용성이 손실됩니다. .NET SDK를 사용하여 여러 지역을 추가하는 방법에 대한 자세한 내용은 여기를 참조하세요.
가용성 및 장애 조치(failover) V3 SDK에서 ApplicationPreferredRegions 또는 ApplicationRegion을 설정하고, 기본 지역 목록을 사용하여 v2 SDK의 PreferredLocations를 설정합니다. 장애 조치(failover) 중에는 쓰기 작업이 현재 쓰기 지역으로 전송되고 모든 읽기가 기본 지역 목록 내의 첫 번째 지역으로 전송됩니다. 지역 장애 조치(failover) 메커니즘에 대한 자세한 내용은 가용성 문제 해결 가이드를 참조하세요.
CPU 클라이언트 컴퓨터에 리소스가 부족하여 연결/가용성 문제가 발생할 수 있습니다. Azure Cosmos DB 클라이언트를 실행하는 노드에서 CPU 사용률을 모니터링하고 사용량이 높은 경우 스케일 업/아웃합니다.
호스팅 가능한 경우, 최상의 성능을 위해 Windows 64비트 호스트 처리를 사용합니다. 직접 모드 대기 시간이 중요한 프로덕션 워크로드의 경우 최대한 최소 4코어 및 8GB 메모리 VM을 사용하는 것이 좋습니다.
연결 모드 최상의 성능을 위해 직접 모드를 사용합니다. 이 작업을 수행하는 방법에 대한 지침은 V3 SDK 설명서 또는 V2 SDK 설명서를 참조하세요.
네트워킹 가상 머신을 사용하여 애플리케이션을 실행하는 경우 VM에서 가속화된 네트워킹을 사용하도록 설정하여 높은 트래픽으로 인한 병목 상태를 지원하고 대기 시간 또는 CPU 지터를 줄입니다. 최대 CPU 사용량이 70% 미만이 되도록 보다 고사양 가상 머신을 사용하는 것을 고려할 수도 있습니다.
임시 포트 고갈 스파스 또는 산발적 연결의 경우, IdleConnectionTimeoutPortReuseModePrivatePortPool로 설정합니다. IdleConnectionTimeout 속성은 사용되지 않는 연결을 닫기 전까지 소요된 시간을 제어하는 데 도움이 됩니다. 이렇게 하면 사용되지 않는 연결 수가 감소합니다. 기본적으로 유휴 연결은 무기한으로 열려 있습니다. 값 설정은 10분보다 크거나 같아야 합니다. 20분~24시간의 값을 권장합니다. PortReuseMode 속성을 사용하면 SDK가 다양한 Azure Cosmos DB 대상 엔드포인트에 작은 임시 포트 풀을 사용할 수 있습니다.
비동기/대기 사용 차단 호출인 Task.Result, Task.Wait, Task.GetAwaiter().GetResult()를 방지합니다. 전체 호출 스택은 비동기/대기 패턴을 활용하기 위해 비동기식입니다. 많은 동기 차단 호출은 스레드 풀 결핍 및 응답 시간 저하로 이어집니다.
엔드투엔드 시간 제한 엔드투엔드 시간 제한을 가져오려면 RequestTimeoutCancellationToken 매개 변수를 모두 사용해야 합니다. 자세한 내용은 시간 제한 문제 해결 가이드를 참조하세요.
재시도 논리 다시 시도할 오류와 SDK에서 다시 시도하는 오류에 대한 자세한 내용은 디자인 지침을 참조하세요. 여러 지역으로 구성된 계정의 경우 SDK가 다른 지역에서 자동으로 다시 시도하는 몇 가지 시나리오가 있습니다. .NET 관련 구현 세부 정보는 SDK 원본 리포지토리를 참조하세요.
데이터베이스/컬렉션 이름 캐싱 구성에서 데이터베이스 및 컨테이너의 이름을 검색하거나 시작할 때 이름을 캐시합니다. ReadDatabaseAsync, ReadDocumentCollectionAsync, CreateDatabaseQuery 또는 CreateDocumentCollectionQuery와 같은 호출은 시스템 예약된 RU 제한을 사용하는 서비스에 대한 메타데이터 호출을 발생시킵니다. CreateIfNotExist는 데이터베이스를 설정하는 데 한 번만 사용해야 합니다. 전체적으로 이 작업은 자주 수행되지 않습니다.
대량 지원 대기 시간을 최적화하지 않아도 되는 시나리오에서 대량의 데이터 덤프를 위한 대량 지원을 사용하는 것이 좋습니다.
병렬 쿼리 Azure Cosmos DB SDK는 쿼리의 대기 시간 및 처리량 향상을 위해 쿼리를 병렬로 실행할 수 있도록 지원합니다. QueryRequestsOptions 내에서 MaxConcurrency 속성은 보유하고 있는 파티션 수로 설정하는 것이 좋습니다. 파티션 수를 모르는 경우에는 가장 짧은 대기 시간을 제공하는 int.MaxValue를 사용하여 시작합니다. 그런 다음, 높은 CPU 문제를 방지하기 위해 환경의 리소스 제한에 도달할 때까지 수를 줄입니다. 또한 MaxBufferedItemCount를 예상되는 반환 결과 수로 설정하여 미리 가져온 결과 수를 제한합니다.
성능 테스트 백오프 애플리케이션에 대한 테스트를 수행하는 경우, RetryAfter 간격에 따라 백오프를 구현해야 합니다. 백오프를 사용하면 재시도 사이 대기 기간을 최소화할 수 있습니다.
인덱싱 Azure Cosmos DB 인덱싱 정책을 통해 인덱싱 경로(IndexingPolicy.IncludedPaths 및 IndexingPolicy.ExcludedPaths)를 사용하여 인덱싱에 포함하거나 제외할 문서 경로를 지정할 수도 있습니다. 쓰기 속도를 높이기 위해 인덱싱에서 사용하지 않는 경로를 제외합니다. SDK를 사용하여 인덱스를 만드는 방법에 대한 자세한 내용은 성능 팁 .NET SDK v3을 참조하세요.
문서 크기 지정된 작업의 요청 요금은 문서의 크기와 직접 관련됩니다. 큰 문서에 대한 작업은 더 작은 문서에 대한 작업보다 비용이 많이 들기 때문에 문서 크기를 더 줄이는 것이 좋습니다.
스레드/작업 수 늘리기 Azure Cosmos DB 호출은 네트워크를 통해 수행되므로 클라이언트 애플리케이션이 요청 간에 대기하는 시간이 최소한이 되도록 요청의 동시 처리 수준을 다양하게 지정해야 할 수 있습니다. 예를 들어 .NET 작업 병렬 라이브러리를 사용하는 경우, Azure Cosmos DB에서 읽거나 쓰는 수백 개의 작업에 대한 순서를 생성합니다.
쿼리 메트릭 사용 백 엔드 쿼리 실행에 대한 추가 로깅을 위해 .NET SDK를 사용하여 SQL 쿼리 메트릭을 사용하도록 설정할 수 있습니다. SQL 쿼리 메트릭을 수집하는 방법에 대한 자세한 내용은 쿼리 메트릭 및 성능을 참조하세요.
SDK 로깅 예외 또는 요청이 예상 대기 시간을 초과하는 경우와 같은 미해결 시나리오에 대한 SDK 진단을 기록합니다.
DefaultTraceListener DefaultTraceListener는 높은 CPU 및 I/O 병목 상태를 일으키는 프로덕션 환경에서 성능 문제를 야기합니다. 최신 SDK 버전을 사용하고 있는지 확인하거나 애플리케이션에서 DefaultTraceListener를 제거합니다.
식별자에 특수 문자를 사용하지 마세요. 일부 문자는 제한되며 일부 식별자 '/', '\', '?', '#'에서 사용할 수 없습니다. 일반적인 권장 사항은 예기치 않은 동작을 방지하기 위해 데이터베이스 이름, 컬렉션 이름, 항목 ID 또는 파티션 키와 같은 식별자에 특수 문자를 사용하지 않는 것입니다.

진단 캡처

CosmosException을 포함하여 SDK의 모든 응답에는 Diagnostics 속성이 있습니다. 이 속성은 재시도 또는 일시적 실패가 있었는지 여부를 포함하여 단일 요청과 관련된 모든 정보를 기록합니다.

진단은 문자열로 반환합니다. 버전마다 문자열이 변경되며, 다양한 시나리오의 문제 해결을 위해 개선되기 때문입니다. 각 SDK 버전에서 문자열의 서식 지정에 대한 호환성이 손상되는 변경이 발생합니다. 호환성이 손상되는 변경이 발생하지 않도록 문자열을 구문 분석하지 마세요. 다음 코드 샘플은 .NET SDK를 사용하여 진단 로그를 읽는 방법을 보여줍니다.

try
{
    ItemResponse<Book> response = await this.Container.CreateItemAsync<Book>(item: testItem);
    if (response.Diagnostics.GetClientElapsedTime() > ConfigurableSlowRequestTimeSpan)
    {
        // Log the response.Diagnostics.ToString() and add any additional info necessary to correlate to other logs 
    }
}
catch (CosmosException cosmosException)
{
    // Log the full exception including the stack trace with: cosmosException.ToString()
    
    // The Diagnostics can be logged separately if required with: cosmosException.Diagnostics.ToString()
}

// When using Stream APIs
ResponseMessage response = await this.Container.CreateItemStreamAsync(partitionKey, stream);
if (response.Diagnostics.GetClientElapsedTime() > ConfigurableSlowRequestTimeSpan || !response.IsSuccessStatusCode)
{
    // Log the diagnostics and add any additional info necessary to correlate to other logs with: response.Diagnostics.ToString()
}

HTTP 연결에 대한 모범 사례

.NET SDK는 HttpClient를 사용하여 구성된 연결 모드에 관계없이 HTTP 요청을 수행합니다. 직접 모드 HTTP는 메타데이터 작업에 사용되며 게이트웨이 모드에서는 데이터 평면 및 메타데이터 작업 모두에 사용됩니다. HttpClient의 기본 사항 중 하나는 HttpClient에서 풀링된 연결 수명을 사용자 지정하여 계정의 DNS 변경 내용에 대응할 수 있도록 하는 것입니다. 풀링된 연결이 열린 상태로 유지되는 한 DNS 변경 내용에 대응하지 않습니다. 이 설정은 주기적으로 풀링된 연결을 강제로 닫아서 애플리케이션이 DNS 변경 내용에 대응하도록 합니다. 연결 모드 및 워크로드에 따라 이 값을 사용자 지정하여 DNS 변경 내용에 대응해야 하는 것(가용성)과 새 연결을 자주 만드는 것 간에 성능 영향의 균형을 맞추는 것이 좋습니다. 특히 게이트웨이 모드의 성능에 영향을 주는 경우 5분 값부터 증가시키는 것이 좋습니다.

예를 들어 CosmosClientOptions.HttpClientFactory를 통해 사용자 지정 HttpClient를 삽입할 수 있습니다.

// Use a Singleton instance of the SocketsHttpHandler, which you can share across any HttpClient in your application
SocketsHttpHandler socketsHttpHandler = new SocketsHttpHandler();
// Customize this value based on desired DNS refresh timer
socketsHttpHandler.PooledConnectionLifetime = TimeSpan.FromMinutes(5);

CosmosClientOptions cosmosClientOptions = new CosmosClientOptions()
{
    // Pass your customized SocketHttpHandler to be used by the CosmosClient
    // Make sure `disposeHandler` is `false`
    HttpClientFactory = () => new HttpClient(socketsHttpHandler, disposeHandler: false)
};

// Use a Singleton instance of the CosmosClient
return new CosmosClient("<connection-string>", cosmosClientOptions);

.NET 종속성 주입을 사용하는 경우 싱글톤 프로세스를 간소화할 수 있습니다.

SocketsHttpHandler socketsHttpHandler = new SocketsHttpHandler();
// Customize this value based on desired DNS refresh timer
socketsHttpHandler.PooledConnectionLifetime = TimeSpan.FromMinutes(5);
// Registering the Singleton SocketsHttpHandler lets you reuse it across any HttpClient in your application
services.AddSingleton<SocketsHttpHandler>(socketsHttpHandler);

// Use a Singleton instance of the CosmosClient
services.AddSingleton<CosmosClient>(serviceProvider =>
{
    SocketsHttpHandler socketsHttpHandler = serviceProvider.GetRequiredService<SocketsHttpHandler>();
    CosmosClientOptions cosmosClientOptions = new CosmosClientOptions()
    {
        HttpClientFactory = () => new HttpClient(socketsHttpHandler, disposeHandler: false)
    };

    return new CosmosClient("<connection-string>", cosmosClientOptions);
});

게이트웨이 모드를 사용하는 경우의 모범 사례

게이트웨이 모드를 사용하는 경우, System.Net MaxConnections를 호스트 단위로 늘립니다. Azure Cosmos DB 요청은 게이트웨이 모드를 사용할 때 HTTPS/REST를 통해 수행됩니다. 호스트 이름이나 IP 주소당 기본 연결 한도가 적용됩니다. 클라이언트 라이브러리가 Azure Cosmos DB에 대한 다중 동시 연결을 활용할 수 있도록 MaxConnections를 더 높은 값(100-1,000)으로 설정해야 할 수도 있습니다. .NET SDK 1.8.0 이상에서 ServicePointManager.DefaultConnectionLimit의 기본값은 50입니다. 값을 변경하기 위해 CosmosClientOptions.GatewayModeMaxConnectionLimit를 더 높은 값으로 설정할 수 있습니다.

쓰기 집약적인 워크로드의 모범 사례

만들기 페이로드가 많은 워크로드의 경우 EnableContentResponseOnWrite요청 옵션을 false로 설정합니다. 서비스에서는 만들거나 업데이트된 리소스를 더는 SDK에 반환하지 않습니다. 일반적으로 애플리케이션에는 생성 중인 개체가 있으므로 서비스에서 반환하지 않아도 됩니다. 요청 요금처럼 헤더 값에 계속 액세스할 수 있습니다. 콘텐츠 응답을 사용하지 않게 설정하면 SDK에서 더는 메모리를 할당하거나 응답 본문을 직렬화할 필요가 없기 때문에 성능 향상에 도움이 될 수 있습니다. 또한 네트워크 대역폭 사용량을 줄여 성능을 개선할 수 있습니다.

Important

EnableContentResponseOnWritefalse로 설정하면 트리거 작업의 응답도 비활성화됩니다.

다중 테넌트 앱에 대한 모범 사례

각 테넌트가 동일한 Azure Cosmos DB 계정 내에서 서로 다른 데이터베이스, 컨테이너 또는 파티션 키로 표시되는 여러 테넌트에 사용량을 분산하는 애플리케이션은 단일 클라이언트 인스턴스를 사용해야 합니다. 단일 클라이언트 인스턴스는 계정 내의 모든 데이터베이스, 컨테이너 및 파티션 키와 상호 작용할 수 있으며 싱글톤 패턴을 사용하는 것이 가장 좋습니다.

그러나 각 테넌트가 다른 Azure Cosmos DB 계정으로 표시되는 경우 계정당 별도의 클라이언트 인스턴스를 만들어야 합니다. 싱글톤 패턴은 여전히 각 클라이언트(애플리케이션 수명 동안 각 계정에 대해 하나의 클라이언트)에 적용되지만 테넌트 양이 많으면 클라이언트 수를 관리하기 어려울 수 있습니다. 연결 수가 컴퓨팅 환경의 제한을 넘어 증가하여 연결 문제가 발생할 수 있습니다.

이러한 경우 다음을 수행하는 것이 좋습니다.

  • 컴퓨팅 환경(CPU 및 연결 리소스)의 제한 사항을 이해합니다. 가능하면 최소 4코어 및 8GB 메모리가 있는 VM을 사용하는 것이 좋습니다.
  • 컴퓨팅 환경의 제한 사항에 따라 단일 컴퓨팅 인스턴스에서 처리할 수 있는 클라이언트 인스턴스 수(따라서 테넌트 수)를 결정합니다. 선택한 연결 모드에 따라 클라이언트당 열릴 연결 수를 예측할 수 있습니다.
  • 인스턴스 간에 테넌트 배포를 평가합니다. 각 컴퓨팅 인스턴스가 제한된 양의 테넌트를 성공적으로 처리할 수 있는 경우 테넌트를 다른 컴퓨팅 인스턴스로 로드 밸런싱하고 라우팅하면 테넌트 수가 증가함에 따라 확장이 가능해집니다.
  • 스파스 워크로드의 경우 가장 사용 빈도가 낮은 캐시를 구조로 사용하여 클라이언트 인스턴스를 보관하고 기간 내에 액세스하지 않은 테넌트의 클라이언트를 삭제하는 것이 좋습니다. .NET의 한 가지 옵션은 MemoryCacheEntryOptions입니다. 여기서 RegisterPostEvictionCallback을 사용하여 비활성 클라이언트를 삭제하고 SetSlidingExpiration을 사용하여 비활성 연결을 보유할 최대 시간을 정의할 수 있습니다.
  • 네트워크 연결 수를 줄이려면 게이트웨이 모드를 사용합니다.
  • 직접 모드를 사용하는 경우 직접 모드 구성에서 CosmosClientOptions.IdleTcpConnectionTimeoutCosmosClientOptions.PortReuseMode를 조정하여 사용되지 않는 연결을 닫고 연결 볼륨을 제어하는 것이 좋습니다.

다음 단계

몇 개의 클라이언트 컴퓨터에서 고성능 시나리오에 대한 Azure Cosmos DB를 평가하는 데 사용된 애플리케이션 예제는 Azure Cosmos DB를 사용한 성능 및 스케일 테스트를 참조하세요.

확장성 및 고성능을 위한 애플리케이션 설계 방법에 대한 자세한 내용은 Azure Cosmos DB의 분할 및 크기 조정을 참조하세요.

Azure Cosmos DB로 마이그레이션하기 위한 용량 계획을 수행하려고 하시나요? 용량 계획을 위해 기존 데이터베이스 클러스터에 대한 정보를 사용할 수 있습니다.