Udostępnij za pośrednictwem


Rozwiązywanie problemów z zapytaniami podczas korzystania z usługi Azure Cosmos DB for MongoDB

DOTYCZY: MongoDB

W tym artykule przedstawiono ogólne zalecane podejście do rozwiązywania problemów z zapytaniami w usłudze Azure Cosmos DB. Chociaż nie należy brać pod uwagę kroków opisanych w tym artykule pełnej ochrony przed potencjalnymi problemami z zapytaniami, uwzględniliśmy tutaj najbardziej typowe porady dotyczące wydajności. Ten artykuł należy użyć jako miejsca początkowego do rozwiązywania problemów z powolnymi lub kosztownymi zapytaniami w interfejsie API usługi Azure Cosmos DB dla bazy danych MongoDB. Jeśli używasz usługi Azure Cosmos DB for NoSQL, zobacz przewodnik rozwiązywania problemów z zapytaniami interfejsu API for NoSQL.

Optymalizacje zapytań w usłudze Azure Cosmos DB są szeroko kategoryzowane w następujący sposób:

  • Optymalizacje, które zmniejszają opłatę za jednostkę żądania (RU) zapytania
  • Optymalizacje, które zmniejszają opóźnienie

Jeśli zmniejszysz opłatę za jednostki RU zapytania, zwykle również zmniejszysz opóźnienie.

Ten artykuł zawiera przykłady, które można utworzyć ponownie przy użyciu zestawu danych żywienia.

Uwaga

W tym artykule założono, że używasz interfejsu API usługi Azure Cosmos DB dla kont bazy danych MongoDB w wersji 3.6 lub nowszej. Niektóre zapytania, które działają słabo w wersji 3.2, mają znaczące ulepszenia w wersjach 3.6 lub nowszych. Uaktualnij do wersji 3.6, wysyłając wniosek o pomoc techniczną.

Uzyskiwanie metryk za pomocą polecenia $explain

Podczas optymalizowania zapytania w usłudze Azure Cosmos DB pierwszym krokiem jest zawsze uzyskanie opłaty za jednostkę RU dla zapytania. Ogólna wytyczna jest taka, że należy poszukać sposobów na obniżenie kosztu zapytań w jednostkach RU z kosztami większymi niż 50 jednostek RU.

Oprócz uzyskania kosztu w jednostkach RU należy użyć polecenia $explain, aby uzyskać metryki użycia zapytania i indeksu. Oto przykład, który uruchamia zapytanie i używa $explain polecenia do wyświetlania metryk użycia zapytań i indeksów:

$explain polecenie:

db.coll.find({foodGroup: "Baby Foods"}).explain({"executionStatistics": true })

Wyjście:

{
    "stages" : [ 
        {
            "stage" : "$query",
            "timeInclusiveMS" : 905.2888,
            "timeExclusiveMS" : 905.2888,
            "in" : 362,
            "out" : 362,
            "details" : {
                "database" : "db-test",
                "collection" : "collection-test",
                "query" : {
                    "foodGroup" : {
                        "$eq" : "Baby Foods"
                    }
                },
                "pathsIndexed" : [],
                "pathsNotIndexed" : [ 
                    "foodGroup"
                ],
                "shardInformation" : [ 
                    {
                        "activityId" : "e68e6bdd-5e89-4ec5-b053-3dbbc2428140",
                        "shardKeyRangeId" : "0",
                        "durationMS" : 788.5867,
                        "preemptions" : 1,
                        "outputDocumentCount" : 362,
                        "retrievedDocumentCount" : 8618
                    }
                ],
                "queryMetrics" : {
                    "retrievedDocumentCount" : 8618,
                    "retrievedDocumentSizeBytes" : 104963042,
                    "outputDocumentCount" : 362,
                    "outputDocumentSizeBytes" : 2553535,
                    "indexHitRatio" : 0.0016802042237178,
                    "totalQueryExecutionTimeMS" : 777.72,
                    "queryPreparationTimes" : {
                        "queryCompilationTimeMS" : 0.19,
                        "logicalPlanBuildTimeMS" : 0.14,
                        "physicalPlanBuildTimeMS" : 0.09,
                        "queryOptimizationTimeMS" : 0.03
                    },
                    "indexLookupTimeMS" : 0,
                    "documentLoadTimeMS" : 687.22,
                    "vmExecutionTimeMS" : 774.09,
                    "runtimeExecutionTimes" : {
                        "queryEngineExecutionTimeMS" : 37.45,
                        "systemFunctionExecutionTimeMS" : 10.82,
                        "userDefinedFunctionExecutionTimeMS" : 0
                    },
                    "documentWriteTimeMS" : 49.42
                }
            }
        }
    ],
    "estimatedDelayFromRateLimitingInMilliseconds" : 0.0,
    "continuation" : {
        "hasMore" : false
    },
    "ok" : 1.0
}

Dane $explain wyjściowe polecenia są długie i zawierają szczegółowe informacje o wykonywaniu zapytań. Zawierają jednak kilka sekcji, na których należy się skupić podczas optymalizowania wydajności zapytań:

Metryczne opis
timeInclusiveMS Opóźnienie zapytania zaplecza
pathsIndexed Pokazuje indeksy używane przez zapytanie
pathsNotIndexed Pokazuje indeksy, których może używać zapytanie, jeśli jest dostępne
shardInformation Podsumowanie wydajności zapytań dla określonej partycji fizycznej
retrievedDocumentCount Liczba dokumentów załadowanych przez aparat zapytań
outputDocumentCount Liczba dokumentów zwróconych w wynikach zapytania
estimatedDelayFromRateLimitingInMilliseconds Szacowane dodatkowe opóźnienie zapytania z powodu ograniczania szybkości

Po pobraniu metryk zapytania porównaj element retrievedDocumentCount z zapytaniem outputDocumentCount . Użyj tego porównania, aby zidentyfikować odpowiednie sekcje do przejrzenia w tym artykule. Jest retrievedDocumentCount to liczba dokumentów, które aparat zapytań musi załadować. Jest outputDocumentCount to liczba dokumentów, które były potrzebne do wyników zapytania. Jeśli wartość retrievedDocumentCount jest znacznie wyższa niż outputDocumentCount, wystąpiła co najmniej jedna część zapytania, która nie mogła użyć indeksu i musiała przeprowadzić skanowanie.

Zapoznaj się z poniższymi sekcjami, aby zrozumieć odpowiednie optymalizacje zapytań dla danego scenariusza.

Opłata za jednostkę ŻĄDANIA zapytania jest zbyt wysoka

Liczba pobranych dokumentów jest znacznie wyższa niż liczba dokumentów wyjściowych

Liczba pobranych dokumentów jest w przybliżeniu równa liczbie dokumentów wyjściowych

Opłaty za jednostki RU zapytania są akceptowalne, ale opóźnienie jest nadal zbyt wysokie

Zapytania, w których liczba pobranych dokumentów przekracza liczbę dokumentów wyjściowych

Jest retrievedDocumentCount to liczba dokumentów potrzebnych do załadowania aparatu zapytań. Jest outputDocumentCount to liczba dokumentów zwracanych przez zapytanie. Jeśli wartość retrievedDocumentCount jest znacznie wyższa niż outputDocumentCount, wystąpiła co najmniej jedna część zapytania, która nie mogła użyć indeksu i musiała przeprowadzić skanowanie.

Oto przykład zapytania skanowania, które nie zostało całkowicie obsłużone przez indeks:

$explain polecenie:

db.coll.find(
  {
    $and : [
            { "foodGroup" : "Cereal Grains and Pasta"}, 
            { "description" : "Oat bran, cooked"}
        ]
  }
).explain({"executionStatistics": true })

Wyjście:

{
    "stages" : [ 
        {
            "stage" : "$query",
            "timeInclusiveMS" : 436.5716,
            "timeExclusiveMS" : 436.5716,
            "in" : 1,
            "out" : 1,
            "details" : {
                "database" : "db-test",
                "collection" : "indexing-test",
                "query" : {
                    "$and" : [ 
                        {
                            "foodGroup" : {
                                "$eq" : "Cereal Grains and Pasta"
                            }
                        }, 
                        {
                            "description" : {
                                "$eq" : "Oat bran, cooked"
                            }
                        }
                    ]
                },
                "pathsIndexed" : [],
                "pathsNotIndexed" : [ 
                    "foodGroup", 
                    "description"
                ],
                "shardInformation" : [ 
                    {
                        "activityId" : "13a5977e-a10a-4329-b68e-87e4f0081cac",
                        "shardKeyRangeId" : "0",
                        "durationMS" : 435.4867,
                        "preemptions" : 1,
                        "outputDocumentCount" : 1,
                        "retrievedDocumentCount" : 8618
                    }
                ],
                "queryMetrics" : {
                    "retrievedDocumentCount" : 8618,
                    "retrievedDocumentSizeBytes" : 104963042,
                    "outputDocumentCount" : 1,
                    "outputDocumentSizeBytes" : 6064,
                    "indexHitRatio" : 0.0,
                    "totalQueryExecutionTimeMS" : 433.64,
                    "queryPreparationTimes" : {
                        "queryCompilationTimeMS" : 0.12,
                        "logicalPlanBuildTimeMS" : 0.09,
                        "physicalPlanBuildTimeMS" : 0.1,
                        "queryOptimizationTimeMS" : 0.02
                    },
                    "indexLookupTimeMS" : 0,
                    "documentLoadTimeMS" : 387.44,
                    "vmExecutionTimeMS" : 432.93,
                    "runtimeExecutionTimes" : {
                        "queryEngineExecutionTimeMS" : 45.36,
                        "systemFunctionExecutionTimeMS" : 16.86,
                        "userDefinedFunctionExecutionTimeMS" : 0
                    },
                    "documentWriteTimeMS" : 0.13
                }
            }
        }
    ],
    "estimatedDelayFromRateLimitingInMilliseconds" : 0.0,
    "continuation" : {
        "hasMore" : false
    },
    "ok" : 1.0
}

Wartość retrievedDocumentCount (8618) jest znacznie wyższa niż outputDocumentCount wartość (1), co oznacza, że to zapytanie wymaga skanowania dokumentu.

Uwzględnij niezbędne indeksy

Należy sprawdzić tablicę pathsNotIndexed i dodać te indeksy. W tym przykładzie ścieżki foodGroup i description powinny być indeksowane.

"pathsNotIndexed" : [ 
                    "foodGroup", 
                    "description"
                ]

Najlepsze rozwiązania dotyczące indeksowania w interfejsie API usługi Azure Cosmos DB dla bazy danych MongoDB różnią się od bazy danych MongoDB. W interfejsie API usługi Azure Cosmos DB dla bazy danych MongoDB indeksy złożone są używane tylko w zapytaniach, które muszą efektywnie sortować według wielu właściwości. Jeśli masz zapytania z filtrami dla wielu właściwości, należy utworzyć pojedyncze indeksy pól dla każdej z tych właściwości. Predykaty zapytań mogą używać wielu indeksów jednego pola.

Indeksy wieloznaczne mogą uprościć indeksowanie. W przeciwieństwie do bazy danych MongoDB indeksy wieloznaczne mogą obsługiwać wiele pól w predykatach zapytań. Nie będzie różnicy w wydajności zapytań, jeśli używasz jednego indeksu wieloznacznych zamiast tworzenia oddzielnego indeksu dla każdej właściwości. Dodanie indeksu wieloznacznych dla wszystkich właściwości jest najprostszym sposobem optymalizacji wszystkich zapytań.

W dowolnym momencie można dodawać nowe indeksy bez wpływu na dostępność zapisu lub odczytu. Możesz śledzić postęp transformacji indeksu.

Informacje o tym, które operacje agregacji używają indeksu

W większości przypadków operacje agregacji w interfejsie API usługi Azure Cosmos DB dla bazy danych MongoDB częściowo używają indeksów. Zazwyczaj aparat zapytań najpierw zastosuje filtry równości i zakresu oraz użyje indeksów. Po zastosowaniu tych filtrów aparat zapytań może ocenić dodatkowe filtry i uciekać się do ładowania pozostałych dokumentów w celu obliczenia agregacji, jeśli jest to konieczne.

Oto przykład:

db.coll.aggregate( [
   { $match: { foodGroup: 'Fruits and Fruit Juices' } },
   {
     $group: {
        _id: "$foodGroup",
        total: { $max: "$version" }
     }
   }
] )

W takim przypadku indeksy mogą zoptymalizować $match etap. Dodanie indeksu dla foodGroup elementu znacznie poprawi wydajność zapytań. Podobnie jak w bazie danych MongoDB, należy jak najszybciej umieścić $match w potoku agregacji, aby zmaksymalizować użycie indeksów.

W interfejsie API usługi Azure Cosmos DB dla bazy danych MongoDB indeksy nie są używane do rzeczywistej agregacji, co w tym przypadku jest .$max Dodanie indeksu w poleceniu version nie poprawi wydajności zapytań.

Zapytania, w których pobrana liczba dokumentów jest równa liczbie dokumentów wyjściowych

Jeśli wartość retrievedDocumentCount jest w przybliżeniu równa outputDocumentCountwartości , aparat zapytań nie musiał skanować wielu niepotrzebnych dokumentów.

Minimalizowanie liczby zapytań między partycjami

Usługa Azure Cosmos DB używa partycjonowania do skalowania poszczególnych kontenerów w miarę zwiększania się zapotrzebowania na jednostkę żądania i magazyn danych. Każda partycja fizyczna ma oddzielny i niezależny indeks. Jeśli zapytanie ma filtr równości zgodny z kluczem partycji kontenera, należy sprawdzić tylko indeks odpowiedniej partycji. Ta optymalizacja zmniejsza łączną liczbę jednostek RU, których wymaga zapytanie. Dowiedz się więcej o różnicach między zapytaniami w partycji i zapytaniami między partycjami.

Jeśli masz dużą liczbę aprowizowanych jednostek RU (ponad 30 000) lub dużą ilość przechowywanych danych (więcej niż około 100 GB), prawdopodobnie masz wystarczająco duży kontener, aby zobaczyć znaczną redukcję opłat za jednostki RU zapytań.

Możesz sprawdzić tablicę, shardInformation aby poznać metryki zapytania dla każdej partycji fizycznej. Liczba unikatowych shardKeyRangeId wartości to liczba partycji fizycznych, w których należy wykonać zapytanie. W tym przykładzie zapytanie zostało wykonane na czterech partycjach fizycznych. Ważne jest, aby zrozumieć, że wykonywanie jest całkowicie niezależne od wykorzystania indeksu. Innymi słowy, zapytania obejmujące wiele partycji mogą nadal używać indeksów.

  "shardInformation" : [ 
                    {
                        "activityId" : "42f670a8-a201-4c58-8023-363ac18d9e18",
                        "shardKeyRangeId" : "5",
                        "durationMS" : 24.3859,
                        "preemptions" : 1,
                        "outputDocumentCount" : 463,
                        "retrievedDocumentCount" : 463
                    }, 
                    {
                        "activityId" : "a8bf762a-37b9-4c07-8ed4-ae49961373c0",
                        "shardKeyRangeId" : "2",
                        "durationMS" : 35.8328,
                        "preemptions" : 1,
                        "outputDocumentCount" : 905,
                        "retrievedDocumentCount" : 905
                    }, 
                    {
                        "activityId" : "3754e36b-4258-49a6-8d4d-010555628395",
                        "shardKeyRangeId" : "1",
                        "durationMS" : 67.3969,
                        "preemptions" : 1,
                        "outputDocumentCount" : 1479,
                        "retrievedDocumentCount" : 1479
                    }, 
                    {
                        "activityId" : "a69a44ee-db97-4fe9-b489-3791f3d52878",
                        "shardKeyRangeId" : "0",
                        "durationMS" : 185.1523,
                        "preemptions" : 1,
                        "outputDocumentCount" : 867,
                        "retrievedDocumentCount" : 867
                    }
                ]

Optymalizacje, które zmniejszają opóźnienie zapytań

W wielu przypadkach koszt w jednostkach RU może być akceptowalny, gdy opóźnienie zapytania jest nadal zbyt duże. W poniższych sekcjach przedstawiono omówienie wskazówek dotyczących zmniejszenia opóźnienia zapytań. Jeśli uruchamiasz to samo zapytanie wiele razy względem tego samego zestawu danych, zwykle koszt w jednostkach RU będzie taki sam. Jednak opóźnienie zapytań może się różnić między ich wykonaniami.

Poprawianie odległości

Zapytania uruchamiane z innego regionu niż konto usługi Azure Cosmos DB będą miały większe opóźnienie niż gdyby były uruchamiane w tym samym regionie. Jeśli na przykład uruchamiasz kod na komputerze stacjonarnym, możesz oczekiwać opóźnienia większego o co najmniej dziesiątki lub setki milisekund niż gdyby zapytanie pochodziło z maszyny wirtualnej w tym samym regionie świadczenia platformy Azure co usługa Azure Cosmos DB. Dane w usłudze Azure Cosmos DB można łatwo dystrybuować globalnie, aby zapewnić zbliżenie danych do aplikacji.

Zwiększanie aprowizowanej przepływności

W usłudze Azure Cosmos DB aprowizowana przepływność jest mierzona w jednostkach żądań (RU). Załóżmy, że masz zapytanie, które zużywa 5 jednostek RU przepływności. Jeśli na przykład aprowizujesz 1000 jednostek RU, możesz uruchomić to zapytanie 200 razy na sekundę. Próba uruchomienia zapytania, gdy nie ma wystarczającej przepływności, skończy się ograniczeniem szybkości przetwarzania żądań przez usługę Azure Cosmos DB. Interfejs API usługi Azure Cosmos DB dla bazy danych MongoDB automatycznie ponowi próbę wykonania tego zapytania po krótkim czasie oczekiwania. Wykonywanie ograniczonych żądań trwa dłużej, więc zwiększenie aprowizowanej przepływności może skrócić opóźnienie zapytań.

Wartość estimatedDelayFromRateLimitingInMilliseconds daje poczucie potencjalnych korzyści z opóźnienia, jeśli zwiększysz przepływność.

Następne kroki