다음을 통해 공유


Azure Cosmos DB 사용 시 문제 해결

적용 대상: NoSQL

이 문서에서는 Azure Cosmos DB의 쿼리 문제를 해결하기 위한 일반적인 권장 방법을 안내합니다. 이 문서에 설명된 단계가 잠재적 쿼리 문제에 대한 완전한 방어로 간주할 수는 없지만 여기에는 가장 일반적인 성능 팁이 포함되어 있습니다. 이 문서를 사용하여 Azure Cosmos DB for NoSQL에서 속도가 느리거나 비용이 많이 드는 쿼리 문제를 해결할 수 있습니다. 또한 진단 로그를 사용하여 속도가 느리거나 상당한 처리량을 사용하는 쿼리를 식별할 수도 있습니다. Azure Cosmos DB의 API for MongoDB를 사용하는 경우 Azure Cosmos DB의 API for MongoDB 쿼리 문제 해결 가이드를 사용해야 합니다.

Azure Cosmos DB의 쿼리 최적화는 광범위하게 다음과 같이 분류됩니다.

  • 쿼리의 RU(요청 단위) 비용을 낮추는 최적화
  • 단지 대기 시간을 단축하는 최적화

쿼리의 RU 비용을 낮출 경우 일반적으로 대기 시간도 단축됩니다.

일반적인 SDK 문제

이 가이드를 읽기 전에 쿼리 엔진과 관련이 없는 일반적인 SDK 문제를 고려하는 것이 좋습니다.

  • 쿼리에 대한 이러한 SDK 성능 팁을 따르세요.
  • 이후 페이지에 결과가 있더라도 간혹 쿼리에 빈 페이지가 있을 수 있습니다. 그 이유는 다음과 같습니다.
    • SDK에서 여러 네트워크 호출을 수행할 수 있습니다.
    • 쿼리가 문서를 검색하는 데 시간이 오래 걸릴 수 있습니다.
  • 모든 쿼리에는 쿼리를 계속할 수 있는 연속 토큰이 있습니다. 쿼리를 완전히 드레이닝해야 합니다. 여러 페이지의 결과 처리에 대한 자세한 정보

쿼리 메트릭 가져오기

Azure Cosmos DB에서 쿼리를 최적화하는 경우 첫 번째 단계는 항상 쿼리에 대한 쿼리 메트릭을 가져오는 것입니다. 이러한 메트릭은 Azure Portal에서도 사용할 수 있습니다. 데이터 탐색기에서 쿼리를 실행하면 결과 탭 옆에 쿼리 메트릭이 표시됩니다.

쿼리 메트릭 가져오기

쿼리 메트릭을 가져온 후 쿼리의 검색된 문서 수출력 문서 수와 비교합니다. 이 비교를 사용하여 이 문서에서 검토할 관련 섹션을 식별할 수 있습니다.

검색된 문서 수는 쿼리 엔진이 로드해야 한 문서 수입니다. 출력 문서 수는 쿼리 결과에 필요한 문서 수입니다. 검색된 문서 수가 출력 문서 수보다 높은 경우 쿼리에 인덱스 사용을 할 수 없고 검사를 수행해야 하는 부분이 하나 이상 있었습니다.

시나리오와 관련된 쿼리 최적화를 이해하려면 다음 섹션을 참조하십시오.

쿼리의 RU 요금이 너무 높음

검색된 문서 수가 출력 문서 수보다 높습니다.


검색된 문서 수와 출력 문서 수가 거의 동일함


쿼리의 RU 요금은 감수할만한 수준이지만 대기 시간이 너무 높음

검색된 문서 수가 출력 문서 수를 초과하는 쿼리

검색된 문서 수는 쿼리 엔진이 로드해야 한 문서 수입니다. 출력 문서 수은 쿼리에서 반환된 문서 수입니다. 검색된 문서 수가 출력 문서 수보다 높은 경우 쿼리에 인덱스 사용을 할 수 없고 검사를 수행해야 하는 부분이 하나 이상 있었습니다.

다음은 인덱스에 의해 완전히 처리되지 않은 검색 쿼리의 예입니다.

쿼리:

SELECT VALUE c.description
FROM c
WHERE UPPER(c.description) = "BABYFOOD, DESSERT, FRUIT DESSERT, WITHOUT ASCORBIC ACID, JUNIOR"

쿼리 메트릭:

Retrieved Document Count                 :          60,951
Retrieved Document Size                  :     399,998,938 bytes
Output Document Count                    :               7
Output Document Size                     :             510 bytes
Index Utilization                        :            0.00 %
Total Query Execution Time               :        4,500.34 milliseconds
  Query Preparation Times
    Query Compilation Time               :            0.09 milliseconds
    Logical Plan Build Time              :            0.05 milliseconds
    Physical Plan Build Time             :            0.04 milliseconds
    Query Optimization Time              :            0.01 milliseconds
  Index Lookup Time                      :            0.01 milliseconds
  Document Load Time                     :        4,177.66 milliseconds
  Runtime Execution Times
    Query Engine Times                   :          322.16 milliseconds
    System Function Execution Time       :           85.74 milliseconds
    User-defined Function Execution Time :            0.00 milliseconds
  Document Write Time                    :            0.01 milliseconds
Client Side Metrics
  Retry Count                            :               0
  Request Charge                         :        4,059.95 RUs

검색된 문서 수(60,951)가 출력 문서 수(7)보다 높으므로 이 쿼리로 인해 문서 검색이 발생했음을 의미합니다. 이 경우 시스템 함수 UPPER()는 인덱스를 사용하지 않습니다.

인덱싱 정책에 필요한 경로를 포함

인덱싱 정책은 WHERE 절, ORDER BY 절, JOIN 및 대부분의 시스템 함수에 포함된 모든 속성을 커버해야 합니다. 인덱스 정책에 지정된 원하는 경로는 JSON 문서의 속성과 일치해야 합니다.

참고 항목

Azure Cosmos DB 인덱싱 정책의 속성은 대/소문자를 구분합니다.

원래 이름

쿼리:

SELECT *
FROM c
WHERE c.description = "Malabar spinach, cooked"

인덱싱 정책:

{
    "indexingMode": "consistent",
    "automatic": true,
    "includedPaths": [
        {
            "path": "/*"
        }
    ],
    "excludedPaths": [
        {
            "path": "/description/*"
        }
    ]
}

RU 요금: 409.51 RU

최적화됨

업데이트된 인덱싱 정책:

{
    "indexingMode": "consistent",
    "automatic": true,
    "includedPaths": [
        {
            "path": "/*"
        }
    ],
    "excludedPaths": []
}

RU 요금: 2.98 RU

쓰기 또는 읽기 가능성에 영향을 주지 않고 언제든지 인덱싱 정책에 속성을 추가할 수 있습니다. 인덱스 변환 진행률을 추적할 수 있습니다.

인덱스를 사용하는 시스템 함수를 파악

대부분의 시스템 함수는 인덱스를 사용합니다. 인덱스를 사용하는 몇 가지 일반적인 문자열 함수 목록은 다음과 같습니다.

  • StartsWith
  • 포함
  • RegexMatch
  • Left
  • Substring - 첫 번째 num_expr이 0인 경우에만 해당

WHERE 절에 사용될 때 인덱스를 사용하지 않고 각 문서를 로드해야 하는 몇 가지 일반적인 시스템 함수는 다음과 같습니다.

시스템 함수 최적화 아이디어
상/하 시스템 함수를 사용하여 비교를 위해 데이터를 정규화하는 대신 삽입 시 대/소문자를 정규화합니다. SELECT * FROM c WHERE UPPER(c.name) = 'BOB'과 같은 쿼리는 SELECT * FROM c WHERE c.name = 'BOB'이 됩니다.
GetCurrentDateTime/GetCurrentTimestamp/GetCurrentTicks 쿼리 실행 전 현재 시간을 계산하고 WHERE 절에서 해당 문자열 값을 사용합니다.
수학 함수(비집계) 쿼리에서 값을 자주 계산해야 하는 경우 JSON 문서에 속성으로 값을 저장하는 것이 좋습니다.

이러한 시스템 함수는 집계가 있는 쿼리에 사용되는 경우를 제외하고 인덱스를 사용할 수 있습니다.

시스템 함수 최적화 아이디어
공간 시스템 함수 실시간 구체화된 뷰에 쿼리 결과 저장

SELECT 절에서 사용되는 경우 비효율적인 시스템 함수는 쿼리에서 인덱스를 사용하는 방법에 영향을 주지 않습니다.

문자열 시스템 함수 실행 개선

인덱스를 사용하는 일부 시스템 함수의 경우 쿼리에 ORDER BY 절을 추가하여 쿼리 실행을 개선할 수 있습니다.

더 구체적으로, 속성의 카디널리티에 따라 RU 요금이 증가하는 시스템 함수는 쿼리에 ORDER BY를 포함하는 것이 유리할 수 있습니다. 이러한 쿼리는 인덱스 검색을 수행하므로 쿼리 결과를 정렬하여 쿼리의 효율성을 높일 수 있습니다.

이러한 최적화는 다음과 같은 시스템 함수의 실행을 향상시킬 수 있습니다.

  • StartsWith(대/소문자 구분 안 함 = true)
  • StringEquals(대/소문자 구분 안 함 = true)
  • 포함
  • RegexMatch
  • EndsWith

예를 들어 CONTAINS를 사용하는 아래 쿼리를 고려하세요. CONTAINS 는 인덱스를 사용하지만 경우에 따라 관련 인덱스를 추가한 후에도 아래 쿼리를 실행할 때 높은 RU 요금이 발생할 수 있습니다.

원래 쿼리:

SELECT *
FROM c
WHERE CONTAINS(c.town, "Sea")

ORDER BY를 추가하여 쿼리 실행을 향상시킬 수 있습니다.

SELECT *
FROM c
WHERE CONTAINS(c.town, "Sea")
ORDER BY c.town

동일한 최적화는 다른 필터를 사용하는 쿼리에 도움이 될 수 있습니다. 이 경우 같음 필터가 있는 속성도 ORDER BY 절에 추가하는 것이 좋습니다.

원래 쿼리:

SELECT *
FROM c
WHERE c.name = "Samer" AND CONTAINS(c.town, "Sea")

(c.name, c.town)에 대한 ORDER BY복합 인덱스를 추가하여 쿼리 실행을 개선할 수 있습니다.

SELECT *
FROM c
WHERE c.name = "Samer" AND CONTAINS(c.town, "Sea")
ORDER BY c.name, c.town

인덱스를 사용하는 집계 쿼리를 파악

대부분의 경우 Azure Cosmos DB의 집계 시스템 함수는 인덱스를 사용합니다. 그러나 집계 쿼리의 필터 또는 다른 절에 따라 쿼리 엔진이 많은 수의 문서를 로드해야 할 수 있습니다. 일반적으로 쿼리 엔진은 같음 및 범위 필터를 먼저 적용합니다. 이러한 필터를 적용한 후 쿼리 엔진은 다른 필터를 평가하고 필요한 경우 나머지 문서를 로드하여 집계를 계산할 수 있습니다.

예를 들어 이러한 두 샘플 쿼리를 고려할 때 같음 및 CONTAINS 시스템 함수 필터를 사용하는 쿼리는 일반적으로 시스템 함수 필터만 있는 CONTAINS 쿼리보다 더 효율적입니다. 이는 비용이 더 많이 드는 CONTAINS 필터를 위해 문서를 로드하기 전에 같음 필터가 먼저 적용되고 인덱스를 사용하기 때문입니다.

CONTAINS 필터만 포함하는 쿼리 - 더 높은 RU 요금:

SELECT COUNT(1)
FROM c
WHERE CONTAINS(c.description, "spinach")

같음 필터와 CONTAINS 필터를 모두 포함하는 쿼리 - 더 낮은 RU 요금:

SELECT AVG(c._ts)
FROM c
WHERE c.foodGroup = "Sausages and Luncheon Meats" AND CONTAINS(c.description, "spinach")

다음은 인덱스를 완전히 사용하지 않는 집계 쿼리의 더 많은 예입니다.

인덱스를 사용하지 않는 시스템 함수를 포함하는 쿼리

관련 시스템 함수의 페이지를 참조하여 인덱스를 사용하는지 확인해야 합니다.

SELECT MAX(c._ts)
FROM c
WHERE CONTAINS(c.description, "spinach")

UDF(사용자 정의 함수)를 포함하는 집계 쿼리

SELECT AVG(c._ts)
FROM c
WHERE udf.MyUDF("Sausages and Luncheon Meats")

GROUP BY를 포함하는 쿼리

절의 속성 GROUP BY 카디널 GROUP BY 리티가 증가함에 따라 쿼리의 RU 요금이 증가합니다. 예를 들어 아래 쿼리에서는 고유 설명 수가 증가함에 따라 쿼리의 RU 요금이 증가합니다.

절이 있는 집계 함수의 RU 요금은 집계 함수 GROUP BY 의 RU 요금보다 높습니다. 다음 예제에서 쿼리 엔진은 c.foodGroup = "Sausages and Luncheon Meats" 필터와 일치하는 모든 문서를 로드해야 합니다. 그러므로 높은 RU 비용이 예상됩니다.

SELECT COUNT(1)
FROM c
WHERE c.foodGroup = "Sausages and Luncheon Meats"
GROUP BY c.description

동일한 집계 쿼리를 자주 실행하려는 경우 개별 쿼리를 실행하는 것보다 Azure Cosmos DB 변경 피드를 사용하여 실시간 구체화된 뷰를 작성하는 것이 더 효율적일 수 있습니다.

필터와 ORDER BY 절을 모두 포함하는 쿼리를 최적화

필터와 ORDER BY 절이 있는 쿼리는 일반적으로 범위 인덱스를 사용하지만 복합 인덱스에서 제공될 수 있는 경우 더 효율적입니다. 인덱싱 정책을 수정하는 것 외에도 복합 인덱스의 모든 속성을 ORDER BY 절에 추가해야 합니다. 쿼리를 변경하면 복합 인덱스가 사용됩니다.

원래 이름

쿼리:

SELECT *
FROM c
WHERE c.foodGroup = "Soups, Sauces, and Gravies"
ORDER BY c._ts ASC

인덱싱 정책:

{

        "automatic":true,
        "indexingMode":"Consistent",
        "includedPaths":[  
            {  
                "path":"/*"
            }
        ],
        "excludedPaths":[]
}

RU 요금: 44.28 RU

최적화됨

업데이트된 쿼리(ORDER BY 절에 두 속성을 포함):

SELECT *
FROM c
WHERE c.foodGroup = "Soups, Sauces, and Gravies"
ORDER BY c.foodGroup, c._ts ASC

업데이트된 인덱싱 정책:

{  
        "automatic":true,
        "indexingMode":"Consistent",
        "includedPaths":[  
            {  
                "path":"/*"
            }
        ],
        "excludedPaths":[],
        "compositeIndexes":[  
            [  
                {  
                    "path":"/foodGroup",
                    "order":"ascending"
        },
                {  
                    "path":"/_ts",
                    "order":"ascending"
                }
            ]
        ]
    }

RU 요금: 8.86 RU

하위 쿼리를 사용하여 JOIN 식을 최적화

다중 값 하위 쿼리는 WHERE 절의 모든 크로스 조인 뒤가 아니라 각 select-many 식 뒤에 조건자를 푸시하여 JOIN 식을 최적화할 수 있습니다.

다음 쿼리를 살펴보십시오.

SELECT Count(1) AS Count
FROM c
JOIN t IN c.tags
JOIN n IN c.nutrients
JOIN s IN c.servings
WHERE t.name = 'infant formula' AND (n.nutritionValue > 0
AND n.nutritionValue < 10) AND s.amount > 1

RU 요금: 167.62 RU

이 쿼리의 경우 인덱스는 이름이 infant formulanutritionValue 0보다 크고 1보다 큰 태그가 있는 모든 문서와 amount 일치합니다. 이 식은 JOIN 필터가 적용되기 전에 일치하는 각 문서에 대한 태그, 영양소 및 인분 배열의 모든 항목의 교차 곱을 수행합니다. 그런 다음 WHERE 절은 각 <c, t, n, s> 튜플에서 필터 조건자를 적용합니다.

예를 들어 일치하는 문서에 세 개의 배열 각각에 10개의 항목이 있는 경우 1 x 10 x 10 x 10(즉, 1,000개) 튜플로 확장됩니다. 여기서 하위 쿼리를 사용하여 다음 식으로 조인하기 전에 조인된 배열 항목을 필터링할 수 있습니다.

다음 쿼리는 위의 쿼리와 동일하지만 하위 쿼리를 사용합니다.

SELECT Count(1) AS Count
FROM c
JOIN (SELECT VALUE t FROM t IN c.tags WHERE t.name = 'infant formula')
JOIN (SELECT VALUE n FROM n IN c.nutrients WHERE n.nutritionValue > 0 AND n.nutritionValue < 10)
JOIN (SELECT VALUE s FROM s IN c.servings WHERE s.amount > 1)

RU 요금: 22.17 RU

태그 배열의 한 항목만 필터와 일치하고 영양소 및 1회 용량 배열 모두 5개 항목이 있다고 가정합니다. 식은 JOIN 첫 번째 쿼리의 1,000개 항목과 달리 1 x 1 x 5 x 5 = 25개 항목으로 확장됩니다.

검색된 문서 수와 출력 문서 수가 동일한 쿼리

검색된 문서 수출력 문서 수가 거의 같으면 쿼리 엔진은 불필요하게 많은 문서를 검색할 필요가 없습니다. TOP 키워드를 사용하는 쿼리를 비롯한 많은 쿼리에서 검색된 문서 수출력 문서 수보다 1이 클 수 있습니다. 이에 대해서는 신경 쓰지 않아도 됩니다.

파티션 간 쿼리를 최소화

Azure Cosmos DB는 요청 단위 및 데이터 스토리지 증가가 필요한 경우 분할을 사용하여 개별 컨테이너를 확장합니다. 각 실제 파티션에는 별도의 독립된 인덱스가 있습니다. 쿼리에 컨테이너의 파티션 키와 일치하는 같음 필터가 있는 경우 관련 파티션의 인덱스만 확인해야 합니다. 이러한 최적화를 통해 쿼리에 필요한 총 RU 수가 줄어듭니다.

프로비전된 RU(30,000개 초과)가 많거나 대용량의 데이터가 저장되어 있는 경우(약 100GB 초과) 컨테이너 크기가 충분히 크다면 쿼리 RU 비용을 크게 줄일 수 있습니다.

예를 들어 파티션 키 foodGroup을 사용하여 컨테이너를 만드는 경우 다음 쿼리는 단일 실제 파티션만 확인해야 합니다.

SELECT *
FROM c
WHERE c.foodGroup = "Soups, Sauces, and Gravies" and c.description = "Mushroom, oyster, raw"

파티션 키가 있는 필터가 있는 쿼리는 IN 하나 이상의 관련 실제 파티션만 확인하고 "팬아웃"하지 않습니다.

SELECT *
FROM c
WHERE c.foodGroup IN("Soups, Sauces, and Gravies", "Vegetables and Vegetable Products") and c.description = "Mushroom, oyster, raw"

파티션 키에 대해 범위 필터를 포함하거나 파티션 키에 필터가 없는 쿼리는 "팬아웃"하고 모든 실제 파티션의 인덱스에서 결과를 확인해야 합니다.

SELECT *
FROM c
WHERE c.description = "Mushroom, oyster, raw"
SELECT *
FROM c
WHERE c.foodGroup > "Soups, Sauces, and Gravies" and c.description = "Mushroom, oyster, raw"

여러 속성에 대한 필터가 있는 쿼리를 최적화

여러 속성에 대한 필터가 있는 쿼리는 일반적으로 범위 인덱스를 사용하지만 복합 인덱스에서 제공될 수 있으면 더 효율적입니다. 소량의 데이터에서는 이러한 최적화가 별 영향을 주지 않습니다. 그러나 대량의 데이터에서는 유용할 수 있습니다. 복합 인덱스당 하나의 같지 않음 필터만 최적화할 수 있습니다. 쿼리에 같지 않음 필터가 여러 개 있는 경우 복합 인덱스를 사용할 필터를 하나 선택합니다. 나머지는 범위 인덱스를 계속 사용합니다. 같지 않음 필터는 복합 인덱스에서 마지막으로 정의되어야 합니다. 복합 인덱스에 대해 자세히 알아보세요.

다음은 복합 인덱스를 사용하여 최적화할 수 있는 쿼리의 몇 가지 예입니다.

SELECT *
FROM c
WHERE c.foodGroup = "Vegetables and Vegetable Products" AND c._ts = 1575503264
SELECT *
FROM c
WHERE c.foodGroup = "Vegetables and Vegetable Products" AND c._ts > 1575503264

다음은 관련 복합 인덱스입니다.

{  
        "automatic":true,
        "indexingMode":"Consistent",
        "includedPaths":[  
            {  
                "path":"/*"
            }
        ],
        "excludedPaths":[],
        "compositeIndexes":[  
            [  
                {  
                    "path":"/foodGroup",
                    "order":"ascending"
                },
                {  
                    "path":"/_ts",
                    "order":"ascending"
                }
            ]
        ]
}

쿼리 대기 시간을 줄이는 최적화

많은 경우에 RU 요금은 감수할만한 수준이지만 쿼리 대기 시간은 매우 높을 수 있습니다. 다음 섹션에서는 쿼리 대기 시간을 줄이기 위한 팁을 개략적으로 설명합니다. 동일한 쿼리를 동일한 데이터 세트에서 여러 번 실행하는 경우 매번 RU 요금은 일반적으로 동일합니다. 하지만 쿼리 대기 시간은 쿼리 실행마다 다를 수 있습니다.

근접성 향상

Azure Cosmos DB 계정과 다른 지역에서 실행되는 쿼리는 동일한 지역 내에서 실행된 경우보다 대기 시간이 더 깁니다. 예를 들어 데스크톱 컴퓨터에서 코드를 실행하는 경우 쿼리가 Azure Cosmos DB와 동일한 Azure 지역 내의 가상 머신에서 나온 것보다 수십 또는 수백 밀리초(또는 그 이상) 높은 대기 시간을 예상해야 합니다. 데이터를 앱에 더 가깝게 가져올 수 있도록 Azure Cosmos DB 데이터를 전 세계적으로 배포하는 것은 간단합니다.

프로비전된 처리량 증가

Azure Cosmos DB에서 프로비전된 처리량은 RU(요청 단위)로 측정됩니다. 5RU의 처리량을 사용하는 쿼리가 있다고 가정합니다. 예를 들어 1,000RU를 프로비전하는 경우 해당 쿼리를 초당 200번 실행할 수 있습니다. 사용 가능한 처리량이 충분하지 않은 경우 쿼리를 실행하려고 하면 Azure Cosmos DB가 HTTP 429 오류를 반환합니다. 모든 최신 API for NoSQL SDK는 잠시 기다린 후 이 쿼리를 자동으로 다시 시도합니다. 제한된 요청은 시간이 오래 걸리므로 프로비전된 처리량을 늘리면 쿼리 대기 시간이 향상될 수 있습니다. Azure Portal의 메트릭 블레이드에서 총 제한 요청 수를 확인할 수 있습니다.

MaxConcurrency 증가

병렬 쿼리는 여러 파티션을 병렬로 쿼리하여 작동합니다. 그러나 개별 분할된 컬렉션의 데이터는 쿼리와 관련하여 순차적으로 가져옵니다. 따라서 MaxConcurrency를 파티션 수로 설정하면 다른 모든 시스템 조건을 동일하게 유지하는 경우 가장 성능이 뛰어난 쿼리를 달성할 수 있습니다. 파티션 수를 모르는 경우 MaxConcurrency(또는 이전 SDK 버전의 MaxDegreesOfParallelism)를 높게 설정할 수 있습니다. 시스템은 최소값(파티션 수, 사용자가 제공한 입력)을 최대 병렬 처리 수준으로 선택합니다.

MaxBufferedItemCount 증가

결과의 현재 배치가 클라이언트에서 처리되는 반면 쿼리는 결과를 프리페치하도록 설계되었습니다. 프리페치를 사용하면 쿼리의 전체 대기 시간을 향상시킬 수 있습니다. MaxBufferedItemCount를 설정하면 프리페치된 결과의 수가 제한됩니다. 이 값을 예상되는 반환 결과 수(또는 더 높은 수)로 설정하면 쿼리는 프리페치를 통해 가장 많은 이점을 얻을 수 있습니다. 이 값을 -1로 설정하면 시스템이 버퍼링할 항목 수를 자동으로 결정합니다.

다음 단계

쿼리당 RU를 측정하고 쿼리를 튜닝하기 위한 실행 통계를 가져오는 방법에 대한 자세한 내용은 다음 문서를 참조하세요.