Otimização do desempenho de consulta com o Azure Cosmos DB

APLICA-SE A: NoSQL

O Azure Cosmos DB fornece uma API para NoSQL para consultar dados, sem exigir esquemas ou índices secundários. Este artigo fornece as seguintes informações para desenvolvedores:

  • Detalhes de alto nível sobre como funciona a execução de consultas SQL do Azure Cosmos DB
  • Dicas e práticas recomendadas para o desempenho da consulta
  • Exemplos de como utilizar métricas de execução de consulta SQL para depurar o desempenho da consulta

Sobre a execução de consultas SQL

No Azure Cosmos DB, os dados são armazenados em contêineres, que podem aumentar para qualquer tamanho de armazenamento ou taxa de transferência de solicitação. O Azure Cosmos DB dimensiona perfeitamente os dados entre partições físicas sob as capas para lidar com o crescimento de dados ou aumentos na taxa de transferência provisionada. Você pode emitir consultas SQL para qualquer contêiner usando a API REST ou um dos SDKs SQL com suporte.

Uma breve visão geral do particionamento: você define uma chave de partição como "cidade", que determina como os dados são divididos entre partições físicas. Os dados pertencentes a uma única chave de partição (por exemplo, "cidade" == "Seattle") são armazenados dentro de uma partição física, e uma única partição física pode armazenar dados de várias chaves de partição. Quando uma partição atinge seu limite de armazenamento, o serviço divide perfeitamente a partição em duas novas partições. Os dados são distribuídos uniformemente pelas novas partições, mantendo todos os dados de uma única chave de partição juntos. Como as partições são transitórias, as APIs usam uma abstração de um intervalo de chaves de partição, que denota os intervalos de hashes de chave de partição.

Quando você emite uma consulta ao Azure Cosmos DB, o SDK executa estas etapas lógicas:

  • Analise a consulta SQL para determinar o plano de execução da consulta.
  • Se a consulta incluir um filtro em relação à chave de partição, como SELECT * FROM c WHERE c.city = "Seattle", ela será roteada para uma única partição. Se a consulta não tiver um filtro na chave de partição, ela será executada em todas as partições e os resultados de cada partição serão mesclados no lado do cliente.
  • A consulta é executada dentro de cada partição em série ou em paralelo, com base na configuração do cliente. Dentro de cada partição, a consulta pode fazer uma ou mais viagens de ida e volta, dependendo da complexidade da consulta, do tamanho da página configurada e da taxa de transferência provisionada da coleção. Cada execução retorna o número de unidades de solicitação consumidas pelas estatísticas de execução de consulta e execução de consulta.
  • O SDK executa um resumo dos resultados da consulta entre partições. Por exemplo, se a consulta envolver um ORDER BY entre partições, os resultados de partições individuais serão classificados em mesclagem para retornar resultados em ordem ordenada globalmente. Se a consulta for uma agregação como COUNT, as contagens de partições individuais serão somadas para produzir a contagem geral.

Os SDKs fornecem várias opções para a execução de consultas. Por exemplo, no .NET, essas opções estão disponíveis na QueryRequestOptions classe. A tabela a seguir descreve essas opções e como elas afetam o tempo de execução da consulta.

Opção Description
EnableScanInQuery Aplicável somente se a indexação para o caminho de filtro solicitado estiver desabilitada. Deve ser definido como true se você optou por não indexar e quiser executar consultas usando uma verificação completa.
MaxItemCount O número máximo de itens a serem devolvidos por viagem de ida e volta ao servidor. Você pode defini-lo como -1 para permitir que o servidor gerencie o número de itens a serem retornados.
MaxBufferedItemCount O número máximo de itens que podem ser armazenados em buffer no lado do cliente durante a execução da consulta paralela. Um valor de propriedade positivo limita o número de itens armazenados em buffer ao valor definido. Você pode defini-lo como menos de 0 para permitir que o sistema decida automaticamente o número de itens a serem armazenados em buffer.
MaxConcurrency Obtém ou define o número de operações simultâneas executadas no lado do cliente durante a execução da consulta paralela. Um valor de propriedade positivo limita o número de operações simultâneas ao valor definido. Você pode defini-lo como menos de 0 para permitir que o sistema decida automaticamente o número de operações simultâneas a serem executadas.
PopulateIndexMetrics Permite a coleta de métricas de índice para entender como o mecanismo de consulta usou índices existentes e como ele poderia usar novos índices em potencial. Essa opção incorre em sobrecarga, portanto, só deve ser habilitada ao depurar consultas lentas.
ResponseContinuationTokenLimitInKb Você pode limitar o tamanho máximo do token de continuação retornado pelo servidor. Talvez seja necessário definir isso se o host do aplicativo tiver limites no tamanho do cabeçalho de resposta, mas isso pode aumentar a duração total e as RUs consumidas para a consulta.

Por exemplo, aqui está uma consulta em um contêiner particionado usando /city o SDK do .NET:

QueryDefinition query = new QueryDefinition("SELECT * FROM c WHERE c.city = 'Seattle'");
QueryRequestOptions options = new QueryRequestOptions()
{
    MaxItemCount = -1,
    MaxBufferedItemCount = -1,
    MaxConcurrency = -1,
    PopulateIndexMetrics = true
};
FeedIterator<dynamic> feedIterator = container.GetItemQueryIterator<dynamic>(query);

FeedResponse<dynamic> feedResponse = await feedIterator.ReadNextAsync();

Cada execução de consulta corresponde a uma API POST REST com cabeçalhos definidos para as opções de solicitação de consulta e a consulta SQL no corpo. Para obter detalhes sobre os cabeçalhos e as opções de solicitação da API REST, consulte Consultando recursos usando a API REST.

Práticas recomendadas para o desempenho da consulta

Os fatores a seguir geralmente têm o maior efeito no desempenho da consulta do Azure Cosmos DB. Aprofundamos cada um desses fatores neste artigo.

Fator Gorjeta
Débito aprovisionado Meça a RU por consulta e verifique se você tem a taxa de transferência provisionada necessária para suas consultas.
Particionamento e chaves de partição Favoreça consultas com o valor da chave de partição na cláusula de filtro para baixa latência.
SDK e opções de consulta Siga as práticas recomendadas do SDK, como conectividade direta, e ajuste as opções de execução de consultas do lado do cliente.
Latência de rede Execute seu aplicativo na mesma região que sua conta do Azure Cosmos DB sempre que possível para reduzir a latência.
Política de indexação Verifique se você tem os caminhos/políticas de indexação necessários para a consulta.
Métricas de execução de consultas Analise as métricas de execução de consulta para identificar possíveis regravações de formas de consulta e dados.

Débito aprovisionado

No Azure Cosmos DB, você cria contêineres de dados, cada um com taxa de transferência reservada expressa em unidades de solicitação (RU) por segundo. Uma leitura de um documento de 1 KB é uma RU, e cada operação (incluindo consultas) é normalizada para um número fixo de RUs com base em sua complexidade. Por exemplo, se você tiver 1000 RU/s provisionados para seu contêiner e tiver uma consulta como SELECT * FROM c WHERE c.city = 'Seattle' essa que consome 5 RUs, poderá executar (1000 RU/s) / (5 RU/query) = 200 dessas consultas por segundo.

Se você enviar mais de 200 consultas/s (ou algumas outras operações que saturam todas as RUs provisionadas), o serviço iniciará solicitações de entrada com limitação de taxa. Os SDKs lidam automaticamente com a limitação de taxa executando um backoff/nova tentativa, portanto, você pode notar uma latência maior para essas consultas. Aumentar a taxa de transferência provisionada para o valor necessário melhora a latência e a taxa de transferência da consulta.

Para saber mais sobre unidades de solicitação, consulte Unidades de solicitação.

Particionamento e chaves de partição

Com o Azure Cosmos DB, os seguintes cenários para leitura de dados são ordenados do que normalmente é mais rápido/mais eficiente para o mais lento/menos eficiente.

  • GET em uma única chave de partição e ID de item, também conhecido como leitura pontual
  • Consulta com uma cláusula de filtro em uma única chave de partição
  • Consulta com uma cláusula de filtro de igualdade ou intervalo em qualquer propriedade
  • Consulta sem filtros

As consultas que precisam ser executadas em todas as partições têm maior latência e podem consumir RUs mais altas. Como cada partição tem indexação automática em relação a todas as propriedades, a consulta pode ser servida de forma eficiente a partir do índice neste caso. Você pode fazer consultas que abrangem partições mais rapidamente usando as opções de paralelismo.

Para saber mais sobre particionamento e chaves de partição, consulte Particionamento no Azure Cosmos DB.

SDK e opções de consulta

Consulte dicas de desempenho de consulta e testes de desempenho para obter o melhor desempenho do lado do cliente do Azure Cosmos DB usando nossos SDKs.

Latência de rede

Consulte Distribuição global do Azure Cosmos DB para saber como configurar a distribuição global e conectar-se à região mais próxima. A latência da rede tem um efeito significativo no desempenho da consulta quando você precisa fazer várias viagens de ida e volta ou recuperar um grande conjunto de resultados da consulta.

Você pode usar métricas de execução de consulta para recuperar o tempo de execução de consultas do servidor, permitindo diferenciar o tempo gasto na execução da consulta do tempo gasto no trânsito de rede.

Política de indexação

Consulte Configurando a política de indexação para trilhos, tipos e modos de indexação e como eles afetam a execução da consulta. Por padrão, o Azure Cosmos DB aplica indexação automática a todos os dados e usa índices de intervalo para cadeias de caracteres e números, o que é eficaz para consultas de igualdade. Para cenários de inserção de alto desempenho, considere excluir caminhos para reduzir o custo de RU para cada operação de inserção.

Você pode usar as métricas de índice para identificar quais índices são usados para cada consulta e se há índices ausentes que melhorariam o desempenho da consulta.

Métricas de execução de consultas

Métricas detalhadas são retornadas para cada execução de consulta no Diagnóstico da solicitação. Essas métricas descrevem onde o tempo é gasto durante a execução da consulta e permitem a solução avançada de problemas.

Saiba mais sobre como obter as métricas de consulta.

Metric Unit Description
TotalTime milissegundos Tempo total de execução da consulta
DocumentLoadTime milissegundos Tempo gasto no carregamento de documentos
DocumentWriteTime milissegundos Tempo gasto escrevendo e serializando os documentos de saída
IndexLookupTime milissegundos Tempo gasto na camada de índice físico
QueryPreparationTime milissegundos Tempo gasto na preparação da consulta
RuntimeExecutionTime milissegundos Tempo total de execução do tempo de execução da consulta
VMExecutionTime milissegundos Tempo gasto no tempo de execução da consulta executando a consulta
OutputDocumentCount contagem Número de documentos de saída no conjunto de resultados
OutputDocumentSize contagem Tamanho total dos documentos gerados em bytes
RetrievedDocumentCount contagem Número total de documentos recuperados
RetrievedDocumentSize bytes Tamanho total dos documentos recuperados em bytes
IndexHitRatio razão [0,1] Proporção entre o número de documentos correspondentes pelo filtro e o número de documentos carregados

Os SDKs de cliente podem fazer internamente várias solicitações de consulta para servir a consulta dentro de cada partição. O cliente faz mais de uma chamada por partição se os resultados totais excederem a opção de solicitação de contagem máxima de itens, se a consulta exceder a taxa de transferência provisionada para a partição, se a carga útil da consulta atingir o tamanho máximo por página ou se a consulta atingir o limite de tempo limite alocado pelo sistema. Cada execução parcial de consulta retorna métricas de consulta para essa página.

Aqui estão algumas consultas de exemplo e como interpretar algumas das métricas retornadas da execução da consulta:

Query Métrica de amostra Description
SELECT TOP 100 * FROM c "RetrievedDocumentCount": 101 O número de documentos recuperados é 100+1 para corresponder à cláusula TOP. O tempo de consulta é gasto principalmente em WriteOutputTime e DocumentLoadTime uma vez que é uma verificação.
SELECT TOP 500 * FROM c "RetrievedDocumentCount": 501 RetrievedDocumentCount agora é maior (500+1 para corresponder à cláusula TOP).
SELECT * FROM c WHERE c.N = 55 "IndexLookupTime": "00:00:00.0009500" Cerca de 0,9 ms é gasto em IndexLookupTime para uma pesquisa de chave, porque é uma pesquisa de índice em /N/?.
SELECT * FROM c WHERE c.N > 55 "IndexLookupTime": "00:00:00.0017700" Um pouco mais de tempo (1,7 ms) gasto em IndexLookupTime em uma verificação de intervalo, porque é uma pesquisa de índice em /N/?.
SELECT TOP 500 c.N FROM c "IndexLookupTime": "00:00:00.0017700" O mesmo tempo gasto em DocumentLoadTime consultas anteriores, mas menor DocumentWriteTime porque estamos projetando apenas um imóvel.
SELECT TOP 500 udf.toPercent(c.N) FROM c "RuntimeExecutionTime": "00:00:00.2136500" Cerca de 213 ms são gastos na RuntimeExecutionTime execução do UDF em cada valor de c.N.
SELECT TOP 500 c.Name FROM c WHERE STARTSWITH(c.Name, 'Den') "IndexLookupTime": "00:00:00.0006400", "RuntimeExecutionTime": "00:00:00.0074100" Cerca de 0,6 ms é gasto em IndexLookupTime /Name/?. A maior parte do tempo de execução da consulta (~7 ms) no RuntimeExecutionTime.
SELECT TOP 500 c.Name FROM c WHERE STARTSWITH(LOWER(c.Name), 'den') "IndexLookupTime": "00:00:00", "RetrievedDocumentCount": 2491, "OutputDocumentCount": 500 A consulta é executada como uma verificação porque usa LOWER, e 500 dos 2491 documentos recuperados são retornados.

Próximos passos

  • Para saber mais sobre os operadores de consulta SQL e palavras-chave suportados, consulte Consulta SQL.
  • Para saber mais sobre unidades de solicitação, consulte Unidades de solicitação.
  • Para saber mais sobre a política de indexação, consulte Política de indexação