Solucionar problemas de consulta ao usar o Azure Cosmos DB for MongoDB
APLICA-SE AO: MongoDB
Este artigo percorre uma abordagem geral recomendada para a solução de problemas de consultas no Azure Cosmos DB. Embora você não deva considerar as etapas descritas neste artigo uma defesa completa contra possíveis problemas de consulta, incluímos as dicas de desempenho mais comuns aqui. Use este artigo como um ponto de partida para solucionar problemas de consultas lentas ou caras na API do Azure Cosmos DB para MongoDB. Se você estiver usando o Azure Cosmos DB para NoSQL, confira o artigo do Guia de solução de problemas de consulta da API do NoSQL.
As otimizações de consulta no Azure Cosmos DB são amplamente categorizadas da seguinte maneira:
- Otimizações que reduzem o preço de RU (unidade de solicitação) da consulta
- Otimizações que apenas reduzem a latência
As otimizações de consulta no Azure Cosmos DB são amplamente categorizadas da seguinte maneira:
Este artigo fornece exemplos que você pode recriar usando o conjunto de dados de nutrição.
Observação
Este artigo pressupõe que você esteja usando a API do Azure Cosmos DB para contas do MongoDB com a versão 3.6 e superior. Algumas consultas com desempenho insatisfatório na versão 3.2 tiveram melhorias significativas nas versões 3.6 e superiores. Atualize para a versão 3.6 enviando uma solicitação de suporte.
Execute o comando $explain para obter métricas:
Quando você otimiza uma consulta no Azure Cosmos DB, a primeira etapa é sempre obter as métricas de preço de RU para a consulta. Uma diretriz aproximada é explorar maneiras de reduzir a carga de RU para consultas com preços acima de 50 RUs.
Além de obter o preço de RU, use o comando $explain
para obter as métricas de uso de consulta e índice. Segue um exemplo que executa uma consulta e usa o comando $explain
para mostrar as métricas de uso de consulta e índice:
Comando $explain:
db.coll.find({foodGroup: "Baby Foods"}).explain({"executionStatistics": true })
Saída:
{
"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
}
A saída do comando $explain
é extensa e tem informações detalhadas sobre a execução da consulta. Porém, em geral, há algumas seções nas quais você deve se concentrar ao otimizar o desempenho da consulta:
Métrica | Descrição |
---|---|
timeInclusiveMS |
Latência de consulta de back-end |
pathsIndexed |
Mostra os índices usados pela consulta |
pathsNotIndexed |
Mostra os índices que a consulta poderia ter usado, se disponíveis |
shardInformation |
Resumo do desempenho da consulta de uma partição física específica |
retrievedDocumentCount |
Número de documentos carregados pelo mecanismo de consulta |
outputDocumentCount |
Número de documentos retornados nos resultados da consulta |
estimatedDelayFromRateLimitingInMilliseconds |
Latência da consulta adicional estimada devido à limitação de taxa |
Depois de obter as métricas de consulta, compare a retrievedDocumentCount
com a outputDocumentCount
para sua consulta. Use essa comparação para identificar as seções relevantes que serão examinadas neste artigo. A retrievedDocumentCount
é o número de documentos que o mecanismo de consulta precisa carregar. A outputDocumentCount
é o número de documentos que foram necessários para os resultados da consulta. Se a retrievedDocumentCount
for significativamente maior do que a outputDocumentCount
, haverá pelo menos uma parte da sua consulta que não conseguiu usar um índice e que precisava fazer uma verificação.
Veja as seções a seguir para entender as otimizações de consulta relevantes para seu cenário.
O preço de RU da consulta é muito alto
A Contagem de Documentos Recuperados é significativamente maior que a Contagem de Documentos de Saída
A Contagem de Documentos Recuperados é aproximadamente igual à Contagem de Documentos de Saída
O preço de RU da consulta é aceitável, mas a latência ainda é muito alta
Consultas em que a contagem de documentos recuperados supera a contagem de documentos de saída
A retrievedDocumentCount
é o número de documentos que o mecanismo de consulta precisava carregar. A outputDocumentCount
é o número de documentos retornados pela consulta. Se a retrievedDocumentCount
for significativamente maior do que a outputDocumentCount
, haverá pelo menos uma parte da sua consulta que não conseguiu usar um índice e que precisava fazer uma verificação.
Veja um exemplo de consulta de verificação que não foi totalmente servida pelo índice:
Comando $explain:
db.coll.find(
{
$and : [
{ "foodGroup" : "Cereal Grains and Pasta"},
{ "description" : "Oat bran, cooked"}
]
}
).explain({"executionStatistics": true })
Saída:
{
"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
}
O retrievedDocumentCount
(8618) é significativamente maior que o outputDocumentCount
(1), o que significa que essa consulta precisava de uma verificação de documento.
Incluir os índices necessários
Verifique a matriz pathsNotIndexed
e adicione esses índices. Neste exemplo, os caminhos foodGroup
e description
precisam ser indexados.
"pathsNotIndexed" : [
"foodGroup",
"description"
]
As práticas recomendadas para indexação na API do Azure Cosmos DB para MongoDB são diferentes do MongoDB. Na API do Azure Cosmos DB para MongoDB, os índices compostos são usados somente em consultas que precisam classificar com eficiência por várias propriedades. Se você tiver consultas com filtros em múltiplas propriedades, crie índices de campo único para cada uma dessas propriedades. Os predicados de consulta podem usar múltiplos índices de campo único.
Índices curingas podem simplificar a indexação. Diferente do MongoDB, os índices curinga podem dar suporte a vários campos em predicados de consulta. Não fará diferença no desempenho da consulta se você usar um único índice curinga em vez de criar um índice separado para cada propriedade. Adicionar um índice curinga para todas as propriedades é a forma mais fácil de otimizar todas as suas consultas.
Você pode adicionar novos índices a qualquer momento, sem afetar a disponibilidade de gravação ou leitura. Você pode acompanhar o progresso da transformação do índice.
Saiba quais operações de agregação usam o índice
Na maioria dos casos, as operações de agregação na API do Azure Cosmos DB para MongoDB usarão os índices parcialmente. Normalmente, o mecanismo de consulta primeiro aplicará filtros de igualdade e intervalo e usará índices. Depois de aplicar esses filtros, o mecanismo de consulta poderá avaliar filtros adicionais e recorrer ao carregamento de documentos restantes para computar a agregação, se necessário.
Veja um exemplo:
db.coll.aggregate( [
{ $match: { foodGroup: 'Fruits and Fruit Juices' } },
{
$group: {
_id: "$foodGroup",
total: { $max: "$version" }
}
}
] )
Nesse caso, os índices podem otimizar o estágio $match
. Adicionar um índice para foodGroup
melhorará significativamente o desempenho da consulta. Como no MongoDB, você deve colocar $match
o quanto antes no pipeline de agregação para maximizar o uso de índices.
Na API do Azure Cosmos DB para o MongoDB, os índices não são usados para a agregação real, que nesse caso é $max
. Adicionar um índice em version
não melhorará o desempenho da consulta.
Consultas cuja Contagem de Documentos Recuperados é igual à Contagem de Documentos de Saída
Se o retrievedDocumentCount
for aproximadamente igual ao outputDocumentCount
, o mecanismo de consulta não precisará verificar muitos documentos desnecessários.
Minimizar consultas entre partições
O Azure Cosmos DB usa particionamento para escalar contêineres individuais à medida que as necessidades de armazenamento de dados e da Unidade de Solicitação aumentam. Cada partição física tem um índice separado e independente. Se a consulta tiver um filtro de igualdade que corresponda à chave de partição do contêiner, você precisará verificar apenas o índice da partição relevante. Essa otimização reduz o número total de RUs que a consulta requer. Saiba mais sobre as diferenças entre as consultas em partição e consultas entre partições.
Se você tiver um grande número de RUs provisionados (mais de 30.000) ou uma grande quantidade de dados armazenados (mais de aproximadamente 100 GB), provavelmente terá um contêiner grande o suficiente para ver uma redução significativa nos preços de RU de consulta.
Verifique a matriz shardInformation
para entender as métricas de consulta para cada partição física individual. O número de valores exclusivos shardKeyRangeId
é o número de partições físicas em que a consulta precisava ser executada. No exemplo, a consulta foi executada em quatro partições físicas. É importante saber que a execução é totalmente independente da utilização do índice. Em outras palavras, consultas entre partições ainda podem usar índices.
"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
}
]
Otimizações que reduzem a latência da consulta
Em muitos casos, o preço de RU pode ser aceitável quando a latência de consulta ainda é muito alta. As seções a seguir fornecem uma visão geral das dicas para reduzir a latência da consulta. Se você executar a mesma consulta várias vezes no mesmo conjunto de dados, ela normalmente terá o mesmo preço de RU. Mas a latência de consulta pode variar entre as execuções de consulta.
Aprimora a proximidade
As consultas executadas de uma região diferente da conta do Azure Cosmos DB terão uma latência maior do que se fossem executadas dentro da mesma região. Por exemplo, se você estiver executando código em seu computador desktop, deverá esperar que a latência seja de dezenas ou centenas de milissegundos maior (ou mais) do que se a consulta vier de uma máquina virtual na mesma região do Azure que o Azure Cosmos DB. É simples distribuir dados globalmente no Azure Cosmos DB para garantir que você possa trazer seus dados mais perto do seu aplicativo.
Aumentar a taxa de transferência provisionada
No Azure Cosmos DB, sua taxa de transferência provisionada é medida em RUs (unidades de solicitação). Imagine que você tenha uma consulta que consome 5 RUs de taxa de transferência. Por exemplo, se você provisionar 1.000 RUs, poderá executar essa consulta 200 vezes por segundo. Se você tentar executar a consulta quando não houver taxa de transferência suficiente disponível, o Azure Cosmos DB limitará a taxa de solicitações. A API do Azure Cosmos DB para o MongoDB repetirá essa consulta automaticamente depois de aguardar por um curto período. As solicitações restritas demoram mais, portanto, aumentar a taxa de transferência provisionada pode aprimorar a latência da consulta.
O valor estimatedDelayFromRateLimitingInMilliseconds
dá uma ideia dos benefícios potenciais de latência se você aumentar a taxa de transferência.
Próximas etapas
- Solucionar problemas de desempenho da consulta (API do NoSQL)
- Evitar a limitação de taxa com SSR
- Gerenciar a indexação na API do Azure Cosmos DB para MongoDB
- Tentando fazer um Planejamento da capacidade para uma migração para o Azure Cosmos DB? Você pode usar informações sobre o cluster de banco de dados existente para fazer isso.
- Se você sabe apenas o número de vCores e servidores no cluster de banco de dados existente, leia sobre como estimar unidades de solicitação com vCores ou vCPUs
- Se souber as taxas de solicitação típicas da carga de trabalho do banco de dados atual, leia sobre como estimar unidades de solicitação usando o planejador de capacidade do Azure Cosmos DB