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 outputDocumentCount
wartoś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
- Rozwiązywanie problemów z wydajnością zapytań (interfejs API dla noSQL)
- Zapobieganie ograniczaniu szybkości za pomocą usługi SSR
- Zarządzanie indeksowaniem w interfejsie API usługi Azure Cosmos DB dla MongoDB
- Próbujesz zaplanować pojemność migracji do usługi Azure Cosmos DB? Informacje o istniejącym klastrze bazy danych można użyć do planowania pojemności.
- Jeśli wiesz, ile rdzeni wirtualnych i serwerów znajduje się w istniejącym klastrze bazy danych, przeczytaj o szacowaniu jednostek żądań przy użyciu rdzeni wirtualnych lub procesorów wirtualnych
- Jeśli znasz typowe stawki żądań dla bieżącego obciążenia bazy danych, przeczytaj o szacowaniu jednostek żądań przy użyciu planisty pojemności usługi Azure Cosmos DB