Partilhar via


Resolver problemas de consultas ao utilizar o Azure Cosmos DB

APLICA-SE A: NoSQL

Este artigo apresenta uma abordagem geral recomendada para a resoluçã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. Você deve usar este artigo como um ponto de partida para solucionar problemas de consultas lentas ou caras no Azure Cosmos DB para NoSQL. Também pode utilizar os registos de diagnóstico para identificar consultas que são lentas ou que consomem um débito significativo. Se você estiver usando a API do Azure Cosmos DB para MongoDB, deverá usar o guia de solução de problemas de consulta da API do Azure Cosmos DB para MongoDB

As otimizações de consulta no Azure Cosmos DB são amplamente categorizadas da seguinte forma:

  • Otimizações que reduzem a cobrança da unidade de solicitação (RU) da consulta
  • Otimizações que apenas reduzem a latência

Se você reduzir a cobrança de RU de uma consulta, normalmente também diminuirá a latência.

Problemas comuns do SDK

Antes de ler este guia, é útil considerar problemas comuns do SDK que não estão relacionados ao mecanismo de consulta.

  • Siga estas dicas de desempenho do SDK para consulta.
  • Por vezes, as consultas podem ter páginas vazias mesmo quando há resultados numa página futura. As razões para tal podem ser:
    • O SDK pode estar fazendo várias chamadas de rede.
    • A consulta pode estar demorando muito para recuperar os documentos.
  • Todas as consultas têm um token de continuação que permite que a consulta continue. Esvazie completamente a consulta. Saiba mais sobre como lidar com várias páginas de resultados

Obter métricas da consulta

Quando você otimiza uma consulta no Azure Cosmos DB, a primeira etapa é sempre obter as métricas de consulta para sua consulta. Essas métricas também estão disponíveis por meio do portal do Azure. Depois de executar a consulta no Data Explorer, as métricas de consulta ficam visíveis ao lado da guia Resultados :

Obter métricas de consulta

Depois de obter as métricas da consulta, compare a Contagem de Documentos Obtidos com a Contagem de Documentos de Saída da sua consulta. Utilize essa comparação para identificar as secções relevantes que devem ser lidas neste artigo.

A Contagem de Documentos Recuperados é o número de documentos que o mecanismo de consulta precisava carregar. A Contagem de Documentos de Saída é o número de documentos necessários para os resultados da consulta. Se a Contagem de Documentos Recuperados for maior do que a Contagem de Documentos de Saída, houve pelo menos uma parte da sua consulta que não pôde usar um índice e precisou fazer uma verificação.

Consulte as seções a seguir para entender as otimizações de consulta relevantes para seu cenário.

A taxa de RU do Query é muito alta

A Contagem de Documentos Recuperados é maior do que a Contagem de Documentos de Saída


A Contagem de Documentos Recuperados é aproximadamente igual à Contagem de Documentos de Saída


A cobrança de RU do Query é aceitável, mas a latência ainda é muito alta

Consultas em que a Contagem de Documentos Recuperados excede a Contagem de Documentos de Saída

A Contagem de Documentos Recuperados é o número de documentos que o mecanismo de consulta precisava carregar. A Contagem de Documentos de Saída é o número de documentos retornados pela consulta. Se a Contagem de Documentos Recuperados for maior do que a Contagem de Documentos de Saída, houve pelo menos uma parte da sua consulta que não pôde usar um índice e precisou fazer uma verificação.

Aqui está um exemplo de consulta de verificação que não foi totalmente atendida pelo índice:

Consulta:

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

Métricas de consulta:

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

A Contagem de Documentos Recuperados (60.951) é maior do que a Contagem de Documentos de Saída (7), o que implica que esta consulta resultou numa digitalização de documentos. Nesse caso, a função do sistema UPPER() não usa um índice.

Incluir os caminhos necessários na política de indexação

Sua política de indexação deve abranger todas as propriedades incluídas em WHERE cláusulas ORDER BY , JOINcláusulas e a maioria das funções do sistema. Os caminhos desejados especificados na política de indexação devem corresponder às propriedades nos documentos JSON.

Nota

As propriedades na política de indexação do Azure Cosmos DB diferenciam maiúsculas de minúsculas

Original

Consulta:

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

Política de Indexação:

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

Custo das RUs: 409,51 RUs

Otimizado

Política de indexação atualizada:

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

Custo das RUs: 2,98 RUs

Você pode adicionar propriedades à política de indexação a qualquer momento, sem efeito na disponibilidade de gravação ou leitura. Você pode acompanhar o progresso da transformação do índice.

Compreender que funções do sistema utilizam o índice

A maioria das funções do sistema usa índices. Aqui está uma lista de algumas funções de cadeia de caracteres comuns que usam índices:

  • StartsWith
  • Contains
  • RegexMatch
  • Left
  • Substring - mas somente se o primeiro num_expr for 0

A seguir estão algumas funções comuns do sistema que não usam o índice e devem carregar cada documento quando usado em uma WHERE cláusula:

Função do sistema Ideias para otimização
Superior/Inferior Em vez de usar a função do sistema para normalizar dados para comparações, normalize o invólucro após a inserção. Uma consulta como SELECT * FROM c WHERE UPPER(c.name) = 'BOB' torna-se SELECT * FROM c WHERE c.name = 'BOB'.
GetCurrentDateTime/GetCurrentTimestamp/GetCurrentTicks Calcule o tempo atual antes da execução da consulta e use esse valor de WHERE cadeia de caracteres na cláusula.
Funções matemáticas (não agregadas) Se você precisar calcular um valor com freqüência em sua consulta, considere armazenar o valor como uma propriedade em seu documento JSON.

Essas funções do sistema podem usar índices, exceto quando usadas em consultas com agregados:

Função do sistema Ideias para otimização
Funções do sistema espacial Armazene o resultado da consulta em uma exibição materializada em tempo real

Quando usadas na cláusula, funções ineficientes do SELECT sistema não afetarão como as consultas podem usar índices.

Melhorar a execução da função do sistema de cadeias

Para algumas funções do sistema que usam índices, você pode melhorar a execução da consulta adicionando uma ORDER BY cláusula à consulta.

Mais especificamente, qualquer função do sistema cuja carga de RU aumenta à medida que a cardinalidade da propriedade aumenta pode se beneficiar de ter ORDER BY na consulta. Essas consultas fazem uma verificação de índice, portanto, ter os resultados da consulta classificados pode tornar a consulta mais eficiente.

Esta otimização pode melhorar a execução das seguintes funções do sistema:

  • StartsWith (onde insensível a maiúsculas e minúsculas = true)
  • StringEquals (onde insensível a maiúsculas e minúsculas = true)
  • Contains
  • RegexMatch
  • EndsWith

Por exemplo, considere a consulta abaixo com CONTAINS. CONTAINS usará índices, mas às vezes, mesmo depois de adicionar o índice relevante, você ainda pode observar uma alta cobrança de RU ao executar a consulta abaixo.

Consulta original:

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

Você pode melhorar a execução da consulta adicionando ORDER BY:

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

A mesma otimização pode ajudar em consultas com outros filtros. Nesse caso, é melhor também adicionar propriedades com filtros de igualdade à ORDER BY cláusula.

Consulta original:

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

Você pode melhorar a execução da consulta adicionando ORDER BY um índice composto para (c.name, c.town):

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

Compreender que consultas agregadas utilizam o índice

Na maioria dos casos, as funções agregadas do sistema no Azure Cosmos DB usam o índice. No entanto, dependendo dos filtros ou de outras cláusulas em uma consulta agregada, o mecanismo de consulta pode ser necessário para carregar um grande número de documentos. Normalmente, o mecanismo de consulta aplica filtros de igualdade e intervalo primeiro. Depois de aplicar esses filtros, o mecanismo de consulta pode avaliar outros filtros e recorrer ao carregamento de documentos restantes para calcular a agregação, se necessário.

Por exemplo, dadas essas duas consultas de exemplo, a consulta com um filtro de igualdade e CONTAINS função do sistema é geralmente mais eficiente do que uma consulta com apenas um filtro de CONTAINS função do sistema. Isso ocorre porque o filtro de igualdade é aplicado primeiro e usa o índice antes que os documentos precisem ser carregados para o filtro mais caro CONTAINS .

Consulta apenas com CONTAINS filtro - maior carga de RU:

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

Consulta com filtro de igualdade e CONTAINS filtro - menor carga de RU:

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

Aqui estão mais exemplos de consultas agregadas que não usarão totalmente o índice:

Consultas com funções do sistema que não usam o índice

Você deve consultar a página da função do sistema relevante para ver se ela usa o índice.

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

Agregar consultas com funções definidas pelo usuário (UDF's)

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

Consultas com GROUP BY

A cobrança de RU de consultas com GROUP BY aumento à medida que a cardinalidade dos imóveis na GROUP BY cláusula aumenta. Na consulta abaixo, por exemplo, a cobrança de RU da consulta aumenta à medida que o número de descrições exclusivas aumenta.

A carga RU de uma função agregada com uma GROUP BY cláusula é maior do que a carga RU de uma função agregada sozinha. Neste exemplo, o mecanismo de consulta deve carregar todos os documentos que correspondem ao c.foodGroup = "Sausages and Luncheon Meats" filtro para que a carga de RU seja alta.

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

Se você planeja executar frequentemente as mesmas consultas agregadas, pode ser mais eficiente criar uma exibição materializada em tempo real com o feed de alterações do Azure Cosmos DB do que executar consultas individuais.

Otimizar consultas que tenham um filtro e uma cláusula ORDER BY

Embora as consultas que têm um filtro e uma ORDER BY cláusula normalmente usem um índice de intervalo, elas são mais eficientes se puderem ser atendidas a partir de um índice composto. Além de modificar a política de indexação, você deve adicionar todas as propriedades no índice composto à ORDER BY cláusula. Essa alteração na consulta garante que ela use o índice composto.

Original

Consulta:

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

Política de Indexação:

{

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

Carga RU: 44,28 RUs

Otimizado

Consulta atualizada (inclui ambas as propriedades na ORDER BY cláusula):

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

Política de indexação atualizada:

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

Carga RU: 8,86 RUs

Otimizar expressões JOIN com uma subconsulta

As subconsultas de vários valores podem otimizar JOIN expressões empurrando predicados após cada expressão select-many, em vez de depois de todas as junções cruzadas na WHERE cláusula.

Considere esta consulta:

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

Carga RU: 167,62 RUs

Para esta consulta, o índice corresponde a qualquer documento que tenha uma marca com o nome infant formula, nutritionValue maior que 0 e amount maior que 1. A JOIN expressão aqui executa o produto cruzado de todos os itens de tags, nutrientes e matrizes de porções para cada documento correspondente antes que qualquer filtro seja aplicado. A WHERE cláusula aplicará então o predicado do filtro em cada <c, t, n, s> tupla.

Por exemplo, se um documento correspondente tiver 10 itens em cada uma das três matrizes, ele se expandirá para 1 x 10 x 10 x 10 (ou seja, 1.000) tuplas. O uso de subconsultas aqui pode ajudar a filtrar itens de matriz associados antes de ingressar com a próxima expressão.

Esta consulta é equivalente à anterior, mas usa subconsultas:

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)

Carga RU: 22,17 RUs

Suponha que apenas um item na matriz de tags corresponde ao filtro e que há cinco itens para as matrizes de nutrientes e porções. As JOIN expressões se expandem para 1 x 1 x 5 x 5 = 25 itens, em oposição a 1.000 itens na primeira consulta.

Consultas em que a Contagem de Documentos Obtidos é igual à Contagem de Documentos Produzidos

Se a Contagem de Documentos Obtidos for aproximadamente igual à Contagem de Documentos Produzidos, o motor de consulta não terá de analisar muitos documentos desnecessários. Para muitas consultas, como aquelas que usam a palavra-chave, a Contagem de Documentos Recuperados pode exceder a TOP Contagem de Documentos de Saída em 1. Não precisa de se preocupar com esta situação.

Minimizar as consultas entre partições

O Azure Cosmos DB usa o particionamento para dimensionar contêineres individuais à medida que a Unidade de Solicitação e as necessidades de armazenamento de dados aumentam. Cada partição física tem um índice separado e independente. Se a sua consulta tiver um filtro de igualdade que corresponda à chave de partição do seu contêiner, você precisará verificar apenas o índice da partição relevante. Esta otimização reduz o número total de RUs necessárias para a consulta.

Se você tiver um grande número de RUs provisionadas (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 nas taxas de RU de consulta.

Por exemplo, se você criar um contêiner com a chave de partição foodGroup, as seguintes consultas precisarão verificar apenas uma única partição física:

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

As consultas que têm um IN filtro com a chave de partição verificam apenas uma ou mais partições físicas relevantes e não "propagam-se":

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

As consultas que têm filtros de intervalo na chave de partição, ou que não têm filtros na chave de partição, precisarão "distribuir" e verificar o índice de cada partição física em busca de resultados:

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"

Otimizar consultas que têm filtros em várias propriedades

Embora as consultas que têm filtros em várias propriedades normalmente usem um índice de intervalo, elas são mais eficientes se puderem ser atendidas a partir de um índice composto. Para pequenas quantidades de dados, esta otimização não terá um impacto significativo. No entanto, pode ser útil para grandes quantidades de dados. Só pode otimizar, no máximo, um filtro de não igualdade por índice composto. Se a consulta tiver vários filtros de não igualdade, escolha um deles que utilizará o índice composto. O resto continua a usar índices de intervalo. O filtro de não-igualdade deve ser definido em último lugar no índice composto. Saiba mais sobre índices compostos.

Aqui estão alguns exemplos de consultas que podem ser otimizadas com um índice composto:

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

Aqui está o índice composto relevante:

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

Otimizações que reduzem a latência da consulta

Em muitos casos, o custo de RU pode ser aceitável quando a latência da consulta ainda é demasiado elevada. As secções seguintes dão uma visão geral das sugestões para reduzir a latência das consultas. Se executar a mesma consulta várias vezes no mesmo conjunto de dados, terá, geralmente, o mesmo custo de RU de cada vez. Contudo, a latência das consultas pode variar entre as execuções das consultas.

Melhorar a proximidade

As consultas executadas de uma região diferente da conta do Azure Cosmos DB têm latência maior do que se fossem executadas dentro da mesma região. Por exemplo, se estiver a executar código no seu computador de secretária, deve esperar que a latência seja dezenas ou centenas de milissegundos mais elevada (ou mais) do que se a consulta viesse de uma máquina virtual na mesma região do Azure Cosmos DB. É simples distribuir dados globalmente no Azure Cosmos DB para garantir que você possa aproximar seus dados do seu aplicativo.

Aumentar a taxa de transferência provisionada

No Azure Cosmos DB, sua taxa de transferência provisionada é medida em Unidades de Solicitação (RUs). Imagine que tem uma consulta que consome 5 RUs de débito. Por exemplo, se aprovisionar 1000 RUs, poderá executar essa consulta 200 vezes por segundo. Se você tentasse executar a consulta quando não houvesse taxa de transferência suficiente disponível, o Azure Cosmos DB retornaria um erro HTTP 429. Qualquer uma das APIs atuais para SDKs NoSQL repetirá automaticamente essa consulta depois de aguardar por um curto período de tempo. Os pedidos com limitação demoram mais tempo, pelo que aumentar o débito aprovisionado pode melhorar a latência das consultas. Você pode observar o número total de solicitações limitadas na folha Métricas do portal do Azure.

Aumentar MaxConcurrency

As consultas paralelas funcionam consultando várias partições em paralelo. Mas os dados de uma coleção particionada individual são obtidos em série em relação à consulta. Assim, se você definir MaxConcurrency para o número de partições, você tem a melhor chance de alcançar a consulta de maior desempenho, desde que todas as outras condições do sistema permaneçam as mesmas. Se você não souber o número de partições, poderá definir MaxConcurrency (ou MaxDegreesOfParallelism em versões mais antigas do SDK) como um número alto. O sistema escolhe o mínimo (número de partições, entrada fornecida pelo usuário) como o grau máximo de paralelismo.

Aumentar MaxBufferedItemCount

As consultas são projetadas para pré-buscar resultados enquanto o lote atual de resultados está sendo processado pelo cliente. A pré-busca ajuda a melhorar a latência geral de uma consulta. A configuração de MaxBufferedItemCount limita o número de resultados pré-buscados. Se você definir esse valor para o número esperado de resultados retornados (ou um número maior), a consulta poderá obter o máximo benefício da pré-busca. Se você definir esse valor como -1, o sistema determinará automaticamente o número de itens a serem armazenados em buffer.

Próximos passos

Consulte os seguintes artigos para obter informações sobre como medir RUs por consulta, obter estatísticas de execução para ajustar suas consultas e muito mais: