Partilhar via


Visão geral da indexação no Azure Cosmos DB

APLICA-SE A: NoSQL MongoDB Cassandra Gremlin Tabela

O Azure Cosmos DB é um banco de dados independente de esquema que permite iterar em seu aplicativo sem ter que lidar com o gerenciamento de esquema ou índice. Por padrão, o Azure Cosmos DB indexa automaticamente todas as propriedades de todos os itens em seu contêiner sem precisar definir nenhum esquema ou configurar índices secundários.

O objetivo deste artigo é explicar como o Azure Cosmos DB indexa os dados e como utiliza os índices para melhorar o desempenho da consulta. Recomenda-se passar por esta seção antes de explorar como personalizar as políticas de indexação.

De itens a árvores

Sempre que um item é armazenado em um contêiner, seu conteúdo é projetado como um documento JSON e, em seguida, convertido em uma representação em árvore. Essa conversão significa que cada propriedade desse item é representada como um nó em uma árvore. Um pseudonó raiz é criado como um pai para todas as propriedades de primeiro nível do item. Os nós de folha contêm os valores escalares reais transportados por um item.

Como exemplo, considere este item:

{
  "locations": [
    { "country": "Germany", "city": "Berlin" },
    { "country": "France", "city": "Paris" }
  ],
  "headquarters": { "country": "Belgium", "employees": 250 },
  "exports": [
    { "city": "Moscow" },
    { "city": "Athens" }
  ]
}

Esta árvore representa o item JSON de exemplo:

Diagrama do item JSON anterior representado como uma árvore.

Observe como as matrizes são codificadas na árvore: cada entrada em uma matriz recebe um nó intermediário rotulado com o índice dessa entrada dentro da matriz (0, 1 etc.).

Das árvores aos caminhos da propriedade

A razão pela qual o Azure Cosmos DB transforma itens em árvores é porque ele permite que o sistema faça referência a propriedades usando seus caminhos dentro dessas árvores. Para obter o caminho para uma propriedade, podemos percorrer a árvore do nó raiz até essa propriedade e concatenar os rótulos de cada nó percorrido.

Aqui estão os caminhos para cada propriedade do item de exemplo descrito anteriormente:

  • /locations/0/country: "Alemanha"
  • /locations/0/city: "Berlim"
  • /locations/1/country: "França"
  • /locations/1/city: "Paris"
  • /headquarters/country: "Bélgica"
  • /headquarters/employees: 250
  • /exports/0/city: "Moscovo"
  • /exports/1/city: "Atenas"

O Azure Cosmos DB indexa efetivamente o caminho de cada propriedade e seu valor correspondente quando um item é gravado.

Tipos de índices

Atualmente, o Azure Cosmos DB dá suporte a três tipos de índices. Você pode configurar esses tipos de índice ao definir a política de indexação.

Índice de intervalo

Os índices de intervalo são baseados em uma estrutura ordenada semelhante a uma árvore. O tipo de índice de intervalo é usado para:

  • Questões de igualdade:

    SELECT * FROM container c WHERE c.property = 'value'
    
    SELECT * FROM c WHERE c.property IN ("value1", "value2", "value3")
    
  • Correspondência de igualdade em um elemento de matriz

    SELECT * FROM c WHERE ARRAY_CONTAINS(c.tags, "tag1")
    
  • Consultas de intervalo:

    SELECT * FROM container c WHERE c.property > 'value'
    

    Nota

    (obras para >, <, >=, , <=) !=

  • Verificação da presença de um imóvel:

    SELECT * FROM c WHERE IS_DEFINED(c.property)
    
  • Funções do sistema de strings:

    SELECT * FROM c WHERE CONTAINS(c.property, "value")
    
    SELECT * FROM c WHERE STRINGEQUALS(c.property, "value")
    
  • ORDER BY Consultas:

    SELECT * FROM container c ORDER BY c.property
    
  • JOIN Consultas:

    SELECT child FROM container c JOIN child IN c.properties WHERE child = 'value'
    

Os índices de intervalo podem ser usados em valores escalares (string ou número). A política de indexação predefinida para os contentores recém-criados impõe índices de intervalo para qualquer cadeia ou número. Para saber como configurar índices de intervalo, consulte Exemplos de política de indexação de intervalo

Nota

Uma ORDER BY cláusula que ordena por uma única propriedade sempre precisa de um índice de intervalo e falhará se o caminho ao qual ela faz referência não tiver um. Da mesma forma, uma ORDER BY consulta ordenada por várias propriedades sempre precisa de um índice composto.

Índice espacial

Os índices espaciais permitem consultas eficientes em objetos geoespaciais, como pontos, linhas, polígonos e multipolígonos. Essas consultas usam ST_DISTANCE, ST_WITHIN ST_INTERSECTS palavras-chave. A seguir estão alguns exemplos que usam o tipo de índice espacial:

  • Consultas de distância geoespacial:

    SELECT * FROM container c WHERE ST_DISTANCE(c.property, { "type": "Point", "coordinates": [0.0, 10.0] }) < 40
    
  • Geoespacial dentro das consultas:

    SELECT * FROM container c WHERE ST_WITHIN(c.property, {"type": "Point", "coordinates": [0.0, 10.0] })
    
  • Consultas de intersecção geoespacial:

    SELECT * FROM c WHERE ST_INTERSECTS(c.property, { 'type':'Polygon', 'coordinates': [[ [31.8, -5], [32, -5], [31.8, -5] ]]  })  
    

Os índices espaciais podem ser usados em objetos GeoJSON formatados corretamente. Atualmente, há suporte para Points, LineStrings, Polygons e MultiPolygons. Para saber como configurar índices espaciais, consulte Exemplos de políticas de indexação espacial

Índices compostos

Os índices compostos aumentam a eficiência quando você executa operações em vários campos. O tipo de índice composto é usado para:

  • ORDER BY Consultas em várias propriedades:

    SELECT * FROM container c ORDER BY c.property1, c.property2
    
  • Consultas com um filtro e ORDER BY. Essas consultas podem utilizar um índice composto se a propriedade filter for adicionada ORDER BY à cláusula.

    SELECT * FROM container c WHERE c.property1 = 'value' ORDER BY c.property1, c.property2
    
  • Consultas com um filtro em duas ou mais propriedades em que pelo menos uma propriedade é um filtro de igualdade

    SELECT * FROM container c WHERE c.property1 = 'value' AND c.property2 > 'value'
    

Contanto que um predicado de filtro use um do tipo de índice, o mecanismo de consulta avalia isso primeiro antes de verificar o restante. Por exemplo, se você tiver uma consulta SQL, como SELECT * FROM c WHERE c.firstName = "Andrew" and CONTAINS(c.lastName, "Liu")

  • A consulta acima filtrará primeiro as entradas em que firstName = "Andrew" usando o índice. Em seguida, ele passa todas as entradas firstName = "Andrew" através de um pipeline subsequente para avaliar o predicado de filtro CONTAINS .

  • Você pode acelerar as consultas e evitar verificações completas de contêiner ao usar funções que executam uma verificação completa como CONTAINS. Você pode adicionar mais predicados de filtro que usam o índice para acelerar essas consultas. A ordem das cláusulas de filtro não é importante. O mecanismo de consulta descobre quais predicados são mais seletivos e executa a consulta de acordo.

Para saber como configurar índices compostos, consulte Exemplos de políticas de indexação composta

Índices vetoriais

Os índices vetoriais aumentam a eficiência ao realizar pesquisas vetoriais usando a função do VectorDistance sistema. As pesquisas de vetores terão latência significativamente menor, maior taxa de transferência e menor consumo de RU ao aproveitar um índice vetorial. Para saber como configurar índices vetoriais, consulte Exemplos de políticas de indexação de vetores

  • ORDER BY Consultas de pesquisa vetorial:

    SELECT TOP 10 *
    FROM c
    ORDER BY VectorDistance(c.vector1, c.vector2)
    
  • Projeção do escore de similaridade em consultas de busca vetorial:

    SELECT TOP 10 c.name, VectorDistance(c.vector1, c.vector2) AS SimilarityScore
    FROM c
    ORDER BY VectorDistance(c.vector1, c.vector2)
    
  • Filtros de intervalo na pontuação de similaridade.

    SELECT TOP 10 *
    FROM c
    WHERE VectorDistance(c.vector1, c.vector2) > 0.8
    ORDER BY VectorDistance(c.vector1, c.vector2)
    

Importante

Atualmente, as políticas vetoriais e os índices vetoriais são imutáveis após a criação. Para fazer alterações, crie uma nova coleção.

Utilização do índice

Há cinco maneiras pelas quais o mecanismo de consulta pode avaliar filtros de consulta, classificados do mais eficiente para o menos eficiente:

  • Busca de índice
  • Varredura precisa do índice
  • Verificação de índice expandida
  • Verificação completa do índice
  • Análise completa

Quando você indexa caminhos de propriedade, o mecanismo de consulta usa automaticamente o índice da forma mais eficiente possível. Além de indexar novos caminhos de propriedade, você não precisa configurar nada para otimizar como as consultas usam o índice. A cobrança de RU de uma consulta é uma combinação da cobrança de RU do uso do índice e da cobrança de RU do carregamento de itens.

Eis uma tabela que resume as diferentes formas como os índices são utilizados no Azure Cosmos DB:

Tipo de pesquisa de índice Description Exemplos comuns Cobrança de RU do uso do índice Cobranças de RU do carregamento de itens do armazenamento de dados transacionais
Busca de índice Somente leitura de valores indexados necessários e carregar somente itens correspondentes do armazenamento de dados transacionais Filtros de igualdade, IN Constante por filtro de igualdade Aumenta com base no número de itens nos resultados da consulta
Varredura precisa do índice Pesquisa binária de valores indexados e carregar apenas itens correspondentes do armazenamento de dados transacionais Comparações de intervalo (>, <, <=, ou >=), StartsWith Comparável à busca de índice, aumenta ligeiramente com base na cardinalidade das propriedades indexadas Aumenta com base no número de itens nos resultados da consulta
Verificação de índice expandida Pesquisa otimizada (mas menos eficiente do que uma pesquisa binária) de valores indexados e carregar apenas itens correspondentes do armazenamento de dados transacionais StartsWith (não diferencia maiúsculas de minúsculas), StringEquals (diferencia maiúsculas de minúsculas) Aumenta ligeiramente com base na cardinalidade das propriedades indexadas Aumenta com base no número de itens nos resultados da consulta
Verificação completa do índice Leia um conjunto distinto de valores indexados e carregue apenas itens correspondentes do armazenamento de dados transacionais Contém, EndsWith, RegexMatch, LIKE Aumenta linearmente com base na cardinalidade das propriedades indexadas Aumenta com base no número de itens nos resultados da consulta
Análise completa Carregar todos os itens do armazenamento de dados transacionais Superior, Inferior N/A Aumentos com base no número de itens no contêiner

Ao escrever consultas, você deve usar predicados de filtro que usam o índice da forma mais eficiente possível. Por exemplo, se um ou StartsWith Contains funcionaria para o seu caso de uso, você deve optar por StartsWith uma vez que ele faz uma verificação de índice precisa em vez de uma verificação de índice completa.

Detalhes de utilização do índice

Nesta seção, abordamos mais detalhes sobre como as consultas usam índices. Esse nível de detalhe não é necessário para aprender a começar a usar o Azure Cosmos DB, mas é documentado em detalhes para usuários curiosos. Fazemos referência ao item de exemplo compartilhado anteriormente neste documento:

Itens de exemplo:

{
  "id": 1,
  "locations": [
    { "country": "Germany", "city": "Berlin" },
    { "country": "France", "city": "Paris" }
  ],
  "headquarters": { "country": "Belgium", "employees": 250 },
  "exports": [
    { "city": "Moscow" },
    { "city": "Athens" }
  ]
}
{
  "id": 2,
  "locations": [
    { "country": "Ireland", "city": "Dublin" }
  ],
  "headquarters": { "country": "Belgium", "employees": 200 },
  "exports": [
    { "city": "Moscow" },
    { "city": "Athens" },
    { "city": "London" }
  ]
}

O Azure Cosmos DB usa um índice invertido. O índice funciona mapeando cada caminho JSON para o conjunto de itens que contêm esse valor. O mapeamento de ID de item é representado em muitas páginas de índice diferentes para o contêiner. Aqui está um diagrama de exemplo de um índice invertido para um contêiner que inclui os dois itens de exemplo:

Caminho Value Lista de IDs de item
/localizações/0/país Alemanha 1
/localizações/0/país Irlanda 2
/localizações/0/cidade Berlim 1
/localizações/0/cidade Dublin 2
/localizações/1/país França 1
/localizações/1/cidade Paris 1
/sede/país Bélgica 1, 2
/sede/funcionários 200 2
/sede/funcionários 250 1

O índice invertido tem dois atributos importantes:

  • Para um determinado caminho, os valores são classificados em ordem crescente. Portanto, o mecanismo de consulta pode servir ORDER BY facilmente a partir do índice.
  • Para um determinado caminho, o mecanismo de consulta pode examinar o conjunto distinto de valores possíveis para identificar as páginas de índice onde há resultados.

O mecanismo de consulta pode utilizar o índice invertido de quatro maneiras diferentes:

Busca de índice

Considere a seguinte consulta:

SELECT location
FROM location IN company.locations
WHERE location.country = 'France'

O predicado de consulta (filtragem em itens em que qualquer local tem "França" como país/região) corresponderia ao caminho destacado neste diagrama:

Diagrama de uma travessia (pesquisa) correspondente a um caminho específico dentro de uma árvore.

Como essa consulta tem um filtro de igualdade, depois de percorrer essa árvore, podemos identificar rapidamente as páginas de índice que contêm os resultados da consulta. Nesse caso, o mecanismo de consulta leria páginas de índice que contêm o Item 1. Uma busca de índice é a maneira mais eficiente de usar o índice. Com uma busca de índice, lemos apenas as páginas de índice necessárias e carregamos apenas os itens nos resultados da consulta. Portanto, o tempo de pesquisa do índice e a carga de RU da pesquisa do índice são incrivelmente baixos, independentemente do volume total de dados.

Varredura precisa do índice

Considere a seguinte consulta:

SELECT *
FROM company
WHERE company.headquarters.employees > 200

O predicado de consulta (filtragem em itens onde há mais de 200 funcionários) pode ser avaliado com uma varredura de índice precisa do headquarters/employees caminho. Ao fazer uma verificação de índice precisa, o mecanismo de consulta começa fazendo uma pesquisa binária do conjunto distinto de valores possíveis para encontrar o local do valor 200 para o headquarters/employees caminho. Como os valores de cada caminho são classificados em ordem crescente, é fácil para o mecanismo de consulta fazer uma pesquisa binária. Depois que o mecanismo de consulta encontrar o valor 200, ele começa a ler todas as páginas de índice restantes (indo na direção crescente).

Como o mecanismo de consulta pode fazer uma pesquisa binária para evitar a varredura de páginas de índice desnecessárias, varreduras de índice precisas tendem a ter latência comparável e encargos de RU para operações de busca de índice.

Verificação de índice expandida

Considere a seguinte consulta:

SELECT *
FROM company
WHERE STARTSWITH(company.headquarters.country, "United", true)

O predicado de consulta (filtragem em itens que têm sede em um local que começa com "United" sem diferenciação de maiúsculas e minúsculas) pode ser avaliado com uma verificação de índice expandida do headquarters/country caminho. As operações que fazem uma verificação de índice expandida têm otimizações que podem ajudar a evitar a necessidade de verificar todas as páginas de índice, mas são um pouco mais caras do que a pesquisa binária de uma varredura de índice precisa.

Por exemplo, ao avaliar que não diferencia maiúsculas de minúsculas StartsWith, o mecanismo de consulta verifica o índice em busca de diferentes combinações possíveis de valores maiúsculos e minúsculos. Essa otimização permite que o mecanismo de consulta evite a leitura da maioria das páginas de índice. Diferentes funções do sistema têm otimizações diferentes que podem ser usadas para evitar a leitura de cada página de índice, por isso são amplamente categorizadas como verificação de índice expandida.

Verificação completa do índice

Considere a seguinte consulta:

SELECT *
FROM company
WHERE CONTAINS(company.headquarters.country, "United")

O predicado de consulta (filtragem em itens que têm sede em um local que contém "United") pode ser avaliado com uma verificação de índice do headquarters/country caminho. Ao contrário de uma verificação de índice precisa, uma verificação de índice completa sempre examina o conjunto distinto de valores possíveis para identificar as páginas de índice onde há resultados. Neste caso, Contains é executado no índice. O tempo de pesquisa do índice e a cobrança de RU para varreduras de índice aumentam à medida que a cardinalidade do caminho aumenta. Em outras palavras, quanto mais valores distintos possíveis que o mecanismo de consulta precisa verificar, maior a latência e a carga de RU envolvidas na execução de uma verificação de índice completa.

Por exemplo, considere duas propriedades: town e country. A cardinalidade da cidade é de 5.000 e a cardinalidade de country é de 200. Aqui estão duas consultas de exemplo que cada uma tem uma função de sistema Contains que faz uma verificação de índice completa na town propriedade. A primeira consulta usa mais RUs do que a segunda consulta porque a cardinalidade da cidade é maior do que country.

SELECT *
FROM c
WHERE CONTAINS(c.town, "Red", false)
SELECT *
FROM c
WHERE CONTAINS(c.country, "States", false)

Análise completa

Em alguns casos, o mecanismo de consulta pode não ser capaz de avaliar um filtro de consulta usando o índice. Nesse caso, o mecanismo de consulta precisa carregar todos os itens do repositório transacional para avaliar o filtro de consulta. As verificações completas não usam o índice e têm uma carga de RU que aumenta linearmente com o tamanho total dos dados. Felizmente, as operações que exigem verificações completas são raras.

Consultas de pesquisa vetorial sem um índice vetorial definido

Se você não definir uma política de índice de vetor e usar a VectorDistance função de sistema em uma ORDER BY cláusula, isso resultará em uma verificação completa e terá uma cobrança de RU maior do que se você definisse uma política de índice de vetor. Similaridade, se você usar VectorDistance com o valor booleano de força bruta definido como true, e não tiver um flat índice definido para o caminho do vetor, ocorrerá uma verificação completa.

Consultas com expressões de filtro complexas

Nos exemplos anteriores, considerávamos apenas consultas que tinham expressões de filtro simples (por exemplo, consultas com apenas um único filtro de igualdade ou intervalo). Na realidade, a maioria das consultas tem expressões de filtro muito mais complexas.

Considere a seguinte consulta:

SELECT *
FROM company
WHERE company.headquarters.employees = 200 AND CONTAINS(company.headquarters.country, "United")

Para executar essa consulta, o mecanismo de consulta deve fazer uma busca de índice e headquarters/employees uma verificação de índice completa em headquarters/country. O mecanismo de consulta tem heurísticas internas que ele usa para avaliar a expressão do filtro de consulta da forma mais eficiente possível. Nesse caso, o mecanismo de consulta evitaria a necessidade de ler páginas de índice desnecessárias fazendo a busca de índice primeiro. Se, por exemplo, apenas 50 itens correspondessem ao filtro de igualdade, o mecanismo de consulta só precisaria avaliar Contains nas páginas de índice que continham esses 50 itens. Uma varredura de índice completa de todo o contêiner não seria necessária.

Utilização de índice para funções de agregado escalar

As consultas com funções agregadas devem depender exclusivamente do índice para usá-lo.

Em alguns casos, o índice pode retornar falsos positivos. Por exemplo, ao avaliar Contains no índice, o número de correspondências no índice pode exceder o número de resultados da consulta. O mecanismo de consulta carrega todas as correspondências de índice, avalia o filtro nos itens carregados e retorna apenas os resultados corretos.

Para a maioria das consultas, o carregamento de correspondências de índice falso positivo não tem nenhum efeito percetível na utilização do índice.

Por exemplo, considere a consulta seguinte:

SELECT *
FROM company
WHERE CONTAINS(company.headquarters.country, "United")

A Contains função do sistema pode retornar algumas correspondências de falsos positivos, portanto, o mecanismo de consulta precisa verificar se cada item carregado corresponde à expressão de filtro. Neste exemplo, o mecanismo de consulta pode precisar carregar apenas alguns itens extras, portanto, o efeito na utilização do índice e na carga de RU é mínimo.

No entanto, consultas com funções agregadas devem depender exclusivamente do índice para usá-lo. Por exemplo, considere a seguinte consulta com uma Count agregação:

SELECT COUNT(1)
FROM company
WHERE CONTAINS(company.headquarters.country, "United")

Como no primeiro exemplo, a função do Contains sistema pode retornar algumas correspondências de falsos positivos. Ao contrário da SELECT * consulta, no entanto, a Count consulta não pode avaliar a expressão de filtro nos itens carregados para verificar todas as correspondências de índice. A Count consulta deve depender exclusivamente do índice, portanto, se houver uma chance de uma expressão de filtro retornar correspondências de falsos positivos, o mecanismo de consulta recorrerá a uma verificação completa.

As consultas com as seguintes funções agregadas devem depender exclusivamente do índice, portanto, avaliar algumas funções do sistema requer uma verificação completa.

Próximos passos

Leia mais sobre indexação nos seguintes artigos: