Azure Cosmos DB .NET SDK에서 느린 요청 진단 및 문제 해결
적용 대상: NoSQL
Azure Cosmos DB에서 요청 속도가 느려질 수 있습니다. 지연은 요청 제한이나 애플리케이션이 설계된 방식과 같은 여러 가지 이유로 인해 발생할 수 있습니다. 이 문서에서는 이 문제의 여러 가지 근본 원인을 설명합니다.
너무 많은 요청 빈도
요청 제한은 느린 요청 문제의 가장 일반적인 원인입니다. Azure Cosmos DB는 요청이 데이터베이스 또는 컨테이너에 할당된 요청 단위를 초과하는 경우 요청을 제한합니다. SDK에는 이러한 요청을 다시 시도하는 기본 제공 논리가 포함되어 있습니다. 너무 많은 요청 빈도 문제 해결 문서에서는 요청이 제한되고 있는지 확인하는 방법을 설명합니다. 이 문서에서는 나중에 이러한 문제를 방지하기 위해 계정을 스케일링하는 방법에 대해서도 설명합니다.
애플리케이션 설계
애플리케이션을 디자인할 때 최상의 성능을 위해 .NET SDK 모범 사례를 따르세요. 애플리케이션이 SDK 모범 사례를 따르지 않으면 요청이 느려지거나 실패할 수 있습니다.
애플리케이션을 개발할 때 다음 사항을 고려해야 합니다.
- 애플리케이션은 Azure Cosmos DB 계정과 동일한 지역에 있어야 합니다.
- ApplicationRegion 또는 ApplicationPreferredRegions는 지역 기본 설정을 반영하고 애플리케이션이 배포된 지역을 가리켜야 합니다.
- 트래픽이 많아 네트워크 인터페이스에서 병목 상태가 발생할 수 있습니다. 애플리케이션이 Azure Virtual Machines에서 실행 중인 경우 가능한 해결 방법이 있습니다.
- 가속 네트워킹이 활성화된 가상 머신 사용을 고려하십시오.
- 기존 가상 머신에서 가속 네트워킹을 활성화합니다.
- 고급 가상 머신을 사용해 보세요.
- 직접 연결 모드를 사용하는 것이 좋습니다.
- CPU 사용량이 너무 높아지지 않도록 합니다. 대부분의 로깅 시스템에서는 기본적으로 평균 사용량이 아닌 최대 CPU 사용량을 확인해야 합니다. 약 40%를 초과하면 대기 시간이 길어질 수 있습니다.
메타데이터 작업
데이터베이스 또는 컨테이너가 있는지 확인해야 하는 경우 항목 작업을 수행하기 전에 Create...IfNotExistsAsync
또는 Read...Async
를 호출하여 확인하지 마세요. 유효성 검사는 삭제될 것으로 예상되는 경우 필요할 때만 애플리케이션 시작 시 수행해야 합니다. 이러한 메타데이터 작업은 추가 대기 시간을 생성하고, SLA(서비스 수준 계약)가 없으며, 별도의 제한 사항이 있습니다. 데이터 작업처럼 확장되지 않습니다.
대량 모드에서 느린 요청
대량 모드는 대기 시간 최적화 모드가 아닌 대용량 데이터 볼륨 작업을 위한 처리량 최적화 모드입니다. 사용 가능한 처리량을 포화하기 위한 것입니다. 대량 모드를 사용할 때 느린 요청이 발생하는 경우 다음을 확인합니다.
- 애플리케이션은 릴리스 구성에서 컴파일됩니다.
- 애플리케이션을 디버깅하는 동안 대기 시간을 측정하지 않습니다(디버거가 연결되지 않음).
- 작업 볼륨이 높으므로 1000개 미만의 작업에 대량을 사용하지 마세요. 프로비전된 처리량은 처리할 수 있는 초당 작업 수를 나타내며 대량의 목표는 최대한 많이 활용하는 것입니다.
- 제한 시나리오에 대한 컨테이너를 모니터링합니다. 컨테이너가 많이 제한되면 데이터 볼륨이 프로비전된 처리량보다 크다는 것을 의미하므로 컨테이너를 확장하거나 데이터 볼륨을 줄여야 합니다(한 번에 더 작은 데이터 일괄 처리를 만들 수 있음).
async/await
패턴을 사용하여 모든 동시 작업을 올바르게 처리하고 비동기 작업을 차단하지 않습니다.
진단 캡처
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()
}
버전 3.19 이상의 진단
SDK 버전마다 JSON 구조에는 호환성이 손상되는 변경이 포함되어 있습니다. 따라서 안전하게 구문 분석할 수 없습니다. JSON은 SDK를 통해 이동하는 요청의 트리 구조를 나타냅니다. 다음 섹션에서는 몇 가지 주요 사항을 살펴봅니다.
CPU 기록
높은 CPU 사용률은 요청 속도가 느려지는 가장 일반적인 원인입니다. 최적 대기 시간을 위해 CPU 사용량은 약 40%가 되어야 합니다. 최대(평균 아님) CPU 사용률을 모니터링하려면 간격으로 10초를 사용합니다. CPU 급증은 요청이 단일 쿼리에 대해 여러 연결을 수행할 수 있는 파티션 간 쿼리에서 더 자주 나타나는 현상입니다.
시간 제한에는 다음과 같은 진단이 포함됩니다.
"systemHistory": [
{
"dateUtc": "2021-11-17T23:38:28.3115496Z",
"cpu": 16.731,
"memory": 9024120.000,
"threadInfo": {
"isThreadStarving": "False",
....
}
},
{
"dateUtc": "2021-11-17T23:38:38.3115496Z",
"cpu": 16.731,
"memory": 9024120.000,
"threadInfo": {
"isThreadStarving": "False",
....
}
},
...
]
cpu
값이 70%를 초과하면 CPU 고갈로 인해 시간 초과가 발생할 수 있습니다. 이 경우 해결 방법은 높은 CPU 사용률의 출처를 조사하여 줄이거나 컴퓨터를 더 큰 리소스 크기로 조정하는 것입니다.threadInfo/isThreadStarving
노드에True
값이 있는 경우 원인은 스레드 부족입니다. 이 경우 해결 방법은 스레드 결핍(잠재적으로 잠긴 스레드)의 출처를 조사하거나 컴퓨터를 더 큰 리소스 크기로 조정하는 것입니다.- 측정 사이의
dateUtc
시간이 약 10초가 아닌 경우 스레드 풀의 경합도 나타냅니다. CPU는 스레드 풀에서 10초마다 큐에 넣는 독립 작업으로 측정됩니다. 측정 사이의 시간이 길면 비동기 작업을 적시에 처리할 수 없다는 것을 나타냅니다. 가장 일반적인 시나리오는 애플리케이션 코드가 비동기 코드에 대한 호출을 차단하는 경우입니다.
솔루션
SDK를 사용하는 클라이언트 애플리케이션을 스케일 업하거나 스케일 아웃해야 합니다.
HttpResponseStats
HttpResponseStats
는 게이트웨이로 이동하는 요청입니다. 심지어 직접 모드에서도 SDK는 게이트웨이에서 모든 메타데이터 정보를 가져옵니다.
요청이 느리면 먼저 이전 제안 중 원하는 결과가 생성되지 않는지 확인합니다. 여전히 속도가 느린 경우 다음과 같은 패턴으로 문제가 발생합니다. 다음 표에 자세한 내용이 나와 있습니다.
요청 수 | 시나리오 | 설명 |
---|---|---|
하나-모두 | 요청 시간 초과 또는 HttpRequestExceptions |
컴퓨터의 SNAT 포트 고갈 또는 리소스 부족으로 인해 요청을 시간 내에 처리할 수 없습니다. |
하나 또는 적은 비율(SLA를 위반하지 않음) | 모두 | 여러 가지 일시적 문제로 인해 요청 중 하나 또는 적은 비율이 느려질 수 있으며 이는 예상할 수 있는 일입니다. |
모두 | 모두 | 인프라 또는 네트워킹에 문제가 있음을 나타냅니다. |
SLA 위반 | 애플리케이션이 변경되지 않고 SLA가 하락 | Azure Cosmos DB 서비스에 문제가 있음을 나타냅니다. |
"HttpResponseStats": [
{
"StartTimeUTC": "2021-06-15T13:53:09.7961124Z",
"EndTimeUTC": "2021-06-15T13:53:09.7961127Z",
"RequestUri": "https://127.0.0.1:8081/dbs/347a8e44-a550-493e-88ee-29a19c070ecc/colls/4f72e752-fa91-455a-82c1-bf253a5a3c4e",
"ResourceType": "Collection",
"HttpMethod": "GET",
"ActivityId": "e16e98ec-f2e3-430c-b9e9-7d99e58a4f72",
"StatusCode": "OK"
}
]
StoreResult
StoreResult
는 TCP 프로토콜을 통해 직접 모드를 사용하는 Azure Cosmos DB에 대한 단일 요청을 나타냅니다.
여전히 속도가 느린 경우 다음과 같은 패턴으로 문제가 발생합니다. 다음 표에 자세한 내용이 나와 있습니다.
요청 수 | 시나리오 | 설명 |
---|---|---|
하나-모두 | StoreResult 에 TransportException 이 포함됨 |
컴퓨터의 SNAT 포트 고갈 또는 리소스 부족으로 인해 요청을 시간 내에 처리할 수 없습니다. |
하나 또는 적은 비율(SLA를 위반하지 않음) | 모두 | 여러 가지 일시적 문제로 인해 요청 중 하나 또는 적은 비율이 느려질 수 있으며 이는 예상할 수 있는 일입니다. |
모두 | 모두 | 인프라 또는 네트워킹에 문제가 있습니다. |
SLA 위반 | 요청에 410 과 같은 여러 실패 오류 코드가 포함되어 있습니다. |
Azure Cosmos DB 서비스 또는 클라이언트 머신에 문제가 있음을 나타냅니다. |
SLA 위반 | StorePhysicalAddress 가 동일하고 실패 없음 상태 코드 |
Azure Cosmos DB에 문제가 있을 수 있습니다. |
SLA 위반 | StorePhysicalAddress 의 파티션 ID는 같지만 복제본 ID가 서로 다르고 실패 없음 상태 코드 |
Azure Cosmos DB에 문제가 있을 수 있습니다. |
SLA 위반 | StorePhysicalAddress 가 임의적이고 실패 없음 상태 코드 |
컴퓨터에 문제가 있음을 나타냅니다. |
단일 요청에 대한 여러 저장소 결과의 경우 다음 사항에 유의하세요.
- 강력한 일관성 및 제한된 부실 일관성은 항상 2개 이상의 저장 결과가 있습니다.
- 각
StoreResult
의 상태 코드를 확인합니다. SDK는 여러 일시적 실패 발생 시 자동으로 다시 시도합니다. SDK는 더 많은 시나리오를 해결하도록 지속적으로 개선됩니다.
RequestTimeline
전송 계층에서 요청을 보내고 받는 여러 스테이지의 시간을 표시합니다.
ChannelAcquisitionStarted
: 새 연결을 획득하거나 만드는 시간입니다. 다음과 같은 여러 가지 이유로 연결을 만들 수 있습니다. 이전 연결이 CosmosClientOptions.IdleTcpConnectionTimeout을 사용한 비활성으로 인해 닫혔습니다. 동시 요청 볼륨이 CosmosClientOptions.MaxRequestsPerTcpConnection을 초과하거나 네트워크 오류로 인해 연결이 닫혔거나 애플리케이션이 싱글톤 패턴을 따르지 않고 새 인스턴스가 지속적으로 만들어집니다. 연결이 설정되면 후속 요청에 다시 사용되므로 앞에서 언급한 문제가 발생하지 않는 한 P99 대기 시간에 영향을 주지 않아야 합니다.Pipelined
: 요청을 TCP 소켓에 기록하는 데 소요된 시간입니다. 요청은 TCP 소켓에 한 번에 하나씩만 기록할 수 있으며, 이 값이 크면 애플리케이션 코드 또는 큰 요청 크기에 의해 잠긴 스레드와 일반적으로 관련된 TCP 소켓의 병목 상태를 나타냅니다.Transit time
: 요청이 TCP 소켓에 기록된 후 네트워크에서 소요된 시간입니다. 이 숫자를BELatencyInMs
와 비교합니다.BELatencyInMs
가 작으면 Azure Cosmos DB 서비스가 아닌 네트워크에서 시간이 소요된 것입니다. 요청이 시간 제한으로 실패한 경우, 클라이언트가 응답 없이 대기한 시간을 나타내며 그 원인은 네트워크 대기 시간입니다.Received
: 응답이 수신된 시점과 SDK에서 처리된 시점 사이의 시간입니다. 일반적으로 큰 값은 스레드 고갈 또는 잠긴 스레드로 인해 발생합니다.
ServiceEndpointStatistics
특정 백 엔드 서버에 대한 정보입니다. SDK는 보류 중인 요청 수 및 MaxConcurrentRequestsPerConnection에 따라 단일 백 엔드 서버에 대한 여러 연결을 열 수 있습니다.
inflightRequests
백 엔드 서버에 대한 보류 중인 요청 수입니다(다른 파티션의 것일 수 있음). 숫자가 많을수록 트래픽이 늘어나고 대기 시간이 늘어날 수 있습니다.openConnections
는 단일 백 엔드 서버에 열려 있는 총 연결 수입니다. 이 숫자는 매우 높은 경우 SNAT 포트 소모를 표시하는 데 유용할 수 있습니다.
ConnectionStatistics
요청이 할당된 특정 연결(신규 또는 이전)에 대한 정보입니다.
waitforConnectionInit
: 현재 요청이 새 연결 초기화가 완료되기를 기다리고 있었습니다. 이로 인해 대기 시간이 늘어나게 됩니다.callsPendingReceive
: 이 호출이 전송되기 전에 수신 보류 중인 호출 수입니다. 숫자가 높으면 이 호출 전에 많은 호출이 있었고 대기 시간이 더 높아질 수 있음을 나타낼 수 있습니다. 이 숫자가 높으면 처리하는 데 시간이 오래 걸리는 쿼리 또는 피드 작업과 같은 다른 요청으로 인해 발생할 수 있는 줄 차단 문제의 헤드를 가리킵니다. CosmosClientOptions.MaxRequestsPerTcpConnection을 낮추어 채널 수를 늘리세요.LastSentTime
: 이 서버로 전송된 마지막 요청 시간입니다. LastReceivedTime과 함께 연결 또는 엔드포인트 문제를 확인하는 데 사용할 수 있습니다. 예를 들어 수신 시간 제한이 많은 경우 보낸 시간은 수신 시간보다 훨씬 큽니다.lastReceive
: 이 서버에서 받은 마지막 요청 시간lastSendAttempt
: 마지막 보내기 시도 시간
요청 및 응답 크기
requestSizeInBytes
: Azure Cosmos DB로 보낸 요청의 총 크기responseMetadataSizeInBytes
: Azure Cosmos DB에서 반환된 헤더의 크기responseBodySizeInBytes
: Azure Cosmos DB에서 반환된 콘텐츠의 크기
"StoreResult": {
"ActivityId": "bab6ade1-b8de-407f-b89d-fa2138a91284",
"StatusCode": "Ok",
"SubStatusCode": "Unknown",
"LSN": 453362,
"PartitionKeyRangeId": "1",
"GlobalCommittedLSN": 0,
"ItemLSN": 453358,
"UsingLocalLSN": true,
"QuorumAckedLSN": -1,
"SessionToken": "-1#453362",
"CurrentWriteQuorum": -1,
"CurrentReplicaSetSize": -1,
"NumberOfReadRegions": 0,
"IsValid": true,
"StorePhysicalAddress": "rntbd://127.0.0.1:10253/apps/DocDbApp/services/DocDbServer92/partitions/a4cb49a8-38c8-11e6-8106-8cdcd42c33be/replicas/1s/",
"RequestCharge": 1,
"RetryAfterInMs": null,
"BELatencyInMs": "0.304",
"transportRequestTimeline": {
"requestTimeline": [
{
"event": "Created",
"startTimeUtc": "2022-05-25T12:03:36.3081190Z",
"durationInMs": 0.0024
},
{
"event": "ChannelAcquisitionStarted",
"startTimeUtc": "2022-05-25T12:03:36.3081214Z",
"durationInMs": 0.0132
},
{
"event": "Pipelined",
"startTimeUtc": "2022-05-25T12:03:36.3081346Z",
"durationInMs": 0.0865
},
{
"event": "Transit Time",
"startTimeUtc": "2022-05-25T12:03:36.3082211Z",
"durationInMs": 1.3324
},
{
"event": "Received",
"startTimeUtc": "2022-05-25T12:03:36.3095535Z",
"durationInMs": 12.6128
},
{
"event": "Completed",
"startTimeUtc": "2022-05-25T12:03:36.8621663Z",
"durationInMs": 0
}
],
"serviceEndpointStats": {
"inflightRequests": 1,
"openConnections": 1
},
"connectionStats": {
"waitforConnectionInit": "False",
"callsPendingReceive": 0,
"lastSendAttempt": "2022-05-25T12:03:34.0222760Z",
"lastSend": "2022-05-25T12:03:34.0223280Z",
"lastReceive": "2022-05-25T12:03:34.0257728Z"
},
"requestSizeInBytes": 447,
"responseMetadataSizeInBytes": 438,
"responseBodySizeInBytes": 604
},
"TransportException": null
}
실패율이 Azure Cosmos DB SLA를 위반합니다.
Azure 지원에 문의하세요.