Indexação no Azure Cosmos DB – Descrição geral

APLICA-SE A: SQL API

Azure Cosmos DB é uma base de dados de esquemas agnósticos que lhe permite iterar a sua aplicação sem ter de lidar com esquemas ou gestão de índices. Por padrão, a Azure Cosmos DB indexa automaticamente todas as propriedades para todos os itens do seu contentor sem ter de definir qualquer 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 secção antes de explorar como personalizar políticas de indexação.

De itens a árvores

Sempre que um item é armazenado num recipiente, o seu conteúdo é projetado como um documento JSON e, em seguida, convertido numa representação de árvore. O que isso significa é que cada propriedade desse item é representada como um nó numa árvore. Um nó de raiz pseudo é criado como um pai para todas as propriedades de primeiro nível do item. Os nós de folha contêm os valores de escalar 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" }
        ]
    }

Seria representada pela seguinte árvore:

O item anterior representado como uma árvore

Note como as matrizes são codificadas na árvore: cada entrada numa matriz recebe um nó intermédio rotulado com o índice dessa entrada dentro da matriz (0, 1 etc.).

Das árvores aos caminhos da propriedade

A razão pela qual a Azure Cosmos DB transforma itens em árvores é porque permite que as propriedades sejam referenciadas pelos seus caminhos dentro dessas árvores. Para obter o caminho para uma propriedade, podemos atravessar a árvore desde o nó raiz até aquela propriedade, e concatenar os rótulos de cada nó atravessado.

Aqui estão os caminhos para cada propriedade a partir do item de exemplo acima descrito:

  • /localizações/0/país: "Alemanha"
  • /localizações/0/cidade: "Berlim"
  • /localizações/1/país: "França"
  • /localizações/1/cidade: "Paris"
  • /sede/país: "Bélgica"
  • /sede/empregados: 250
  • /exportações/0/cidade: "Moscovo"
  • /exportações/1/cidade: "Atenas"

Quando um item é escrito, a Azure Cosmos DB indexa eficazmente o caminho de cada propriedade e o seu valor correspondente.

Tipos de índices

A Azure Cosmos DB suporta atualmente três tipos de índices. Pode configurar estes tipos de índices ao definir a política de indexação.

Índice de Gama

O índice de alcance baseia-se numa estrutura ordenada em forma de árvore. O tipo de índice de gama é utilizado para:

  • Consultas de igualdade:

       SELECT * FROM container c WHERE c.property = 'value'
    
    SELECT * FROM c WHERE c.property IN ("value1", "value2", "value3")
    

    Igualdade jogo em um elemento de matriz

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

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

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

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

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

    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 gama podem ser usados em valores escalares (cadeia 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 aprender a configurar índices de gama, consulte exemplos de política de indexação de gama

Nota

Uma ORDER BY cláusula que ordena por uma única propriedade sempre precisa de um índice de alcance e falhará se o caminho que referenciar não tiver um. Da mesma forma, uma ORDER BY consulta que encomenda por múltiplas propriedades sempre precisa de um índice composto.

Índice espacial

Os índices espaciais permitem consultas eficientes em objetos geoespaciais tais como - pontos, linhas, polígonos e multipolígonos. Estas consultas usam ST_DISTANCE, ST_WITHIN, ST_INTERSECTS palavras-chave. Seguem-se alguns exemplos que utilizam o tipo de índice espacial:

  • Consultas de distância geoespaciais:

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

    SELECT * FROM container c WHERE ST_WITHIN(c.property, {"type": "Point", "coordinates": [0.0, 10.0] })
    
  • Consultas intersectais geoespaciais:

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

Os índices espaciais podem ser utilizados em objetos GeoJSON corretamente formatados. Pontos, LineStrings, Polígonos e MultiPolygons são atualmente suportados. Para utilizar este tipo de índice, definido utilizando a "kind": "Range" propriedade ao configurar a política de indexação. Para aprender a configurar índices espaciais, consulte exemplos de política de indexação espacial

Índices compostos

Os índices compósitos aumentam a eficiência quando se está a realizar operações em vários campos. O tipo de índice composto é utilizado para:

  • ORDER BY consultas sobre múltiplas propriedades:
 SELECT * FROM container c ORDER BY c.property1, c.property2
  • Consultas com um filtro e ORDER BY . Estas consultas podem utilizar um índice composto se a propriedade do filtro 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 onde pelo menos uma propriedade é um filtro de igualdade
 SELECT * FROM container c WHERE c.property1 = 'value' AND c.property2 > 'value'

Enquanto um pré-filtro utilizar um do tipo de índice, o motor de consulta avaliará primeiro antes de digitalizar o resto. Por exemplo, se tiver uma consulta SQL, comoSELECT * FROM c WHERE c.firstName = "Andrew" and CONTAINS(c.lastName, "Liu")

  • A consulta acima filtra-se primeiro para entradas onde primeiro Nome = "Andrew" utilizando o índice. Em seguida, passa todas as entradas do primeiro Nome = "Andrew" através de um pipeline subsequente para avaliar o predicado do filtro CONTAINS.

  • Pode acelerar as consultas e evitar análises completas dos contentores quando utilizar funções que não utilizem o índice (por exemplo, CONTAINS) adicionando predicados de filtros adicionais que utilizam o índice. A ordem das cláusulas de filtragem não é importante. O motor de consulta é vai descobrir quais os predicados são mais seletivos e executar a consulta em conformidade.

Para aprender a configurar índices compostos, consulte exemplos de política de indexação compósita

Utilização de índices

Existem cinco maneiras de o motor de consulta avaliar os filtros de consulta, classificados pelo mais eficiente e menos eficiente:

  • Procura de índice
  • Digitalização precisa do índice
  • Digitalização de índice expandida
  • Digitalização completa do índice
  • Análise completa

Quando indexar os percursos de propriedade, o motor de consulta utilizará automaticamente o índice da forma mais eficiente possível. Além de indexar novos caminhos de propriedade, não é necessário configurar nada para otimizar a forma como as consultas usam o índice. A taxa RU de uma consulta é uma combinação tanto da carga RU da utilização do índice como da carga RU de itens de carregamento.

Aqui está uma tabela que resume as diferentes formas como os índices são usados em Azure Cosmos DB:

Tipo de procura de índice Description Exemplos comuns Taxa RU da utilização do índice Taxa RU de carregamento de itens da loja de dados transacionais
Procura de índice Leia apenas os valores indexados necessários e carregue apenas itens correspondentes da loja de dados transacionais Filtros para a igualdade, IN Filtro constante por igualdade Aumentos com base no número de itens nos resultados da consulta
Digitalização precisa do índice Procura binária de valores indexados e carga apenas itens correspondentes a partir da loja de dados transacionais Comparações de gama (>, <, <=ou >=), StartsWith Comparável à procura de índice, aumenta ligeiramente com base na cardinalidade das propriedades indexadas Aumentos com base no número de itens nos resultados da consulta
Digitalização de índice expandida Pesquisa otimizada (mas menos eficiente do que uma pesquisa binária) de valores indexados e carga apenas itens correspondentes a partir da loja de dados transacionais StartWith (caso-insensível), StringEquals (caso-insensível) Aumenta ligeiramente com base na cardinalidade das propriedades indexadas Aumentos com base no número de itens nos resultados da consulta
Digitalização completa do índice Leia um conjunto distinto de valores indexados e carregue apenas itens correspondentes da loja de dados transacionais Contém, EndsWith, RegexMatch, LIKE Aumenta linearmente com base na cardinalidade das propriedades indexadas Aumentos com base no número de itens nos resultados da consulta
Análise completa Carregue todos os itens da loja de dados transacionais Superior, Inferior N/D Aumentos com base no número de itens em contentores

Ao escrever consultas, deve utilizar o pré-filtro predicado que utilize o índice da forma mais eficiente possível. Por exemplo, se um ou outro StartsWith trabalhar para o seu caso de Contains uso, deve optar StartsWith por, uma vez que irá fazer uma verificação de índice precisa em vez de uma verificação completa do índice.

Detalhes de utilização do índice

Nesta secção, vamos cobrir mais detalhes sobre como as consultas usam índices. Isto não é necessário para aprender a começar com a Azure Cosmos DB, mas está documentado em detalhe para utilizadores curiosos. Vamos fazer referência ao item de exemplo partilhado anteriormente neste documento:

Exemplos de itens:

    {
        "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" }
        ]
    }

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 do item está representado em muitas páginas de índice diferentes para o recipiente. Aqui está um diagrama de amostra de um índice invertido para um recipiente que inclui os dois itens de exemplo:

Caminho Valor 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/empregados 200 2
/sede/empregados 250 1

O índice invertido tem dois atributos importantes:

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

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

Procura de índice

Considere a consulta seguinte:

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

O predicado de consulta (filtragem em itens onde qualquer local tenha "França" como seu país/região) corresponderia ao caminho realçado a vermelho abaixo:

Combinando um caminho específico dentro de uma árvore

Uma vez que esta consulta tem um filtro de igualdade, depois de atravessar esta árvore, podemos identificar rapidamente as páginas de índice que contêm os resultados da consulta. Neste caso, o motor de consulta leria páginas de índice que contêm o Ponto 1. Um índice procura é a forma mais eficiente de usar o índice. Com uma procura de índice, apenas lemos as páginas de índice necessárias e carregamos apenas os itens nos resultados da consulta. Portanto, o tempo de procura do índice e a carga RU da procura de índices são incrivelmente baixos, independentemente do volume total de dados.

Digitalização precisa do índice

Considere a consulta seguinte:

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

O predicado de consulta (filtragem em itens onde existem mais de 200 colaboradores) pode ser avaliado com uma análise precisa do índice do headquarters/employees caminho. Ao fazer uma verificação de índice preciso, o motor de consulta começa por fazer uma pesquisa binária do conjunto distinto de valores possíveis para encontrar a localização do valor 200 para o headquarters/employees caminho. Uma vez que os valores de cada caminho são classificados em ordem ascendente, é fácil para o motor de consulta fazer uma pesquisa binária. Depois de o motor de consulta encontrar o 200 valor, começa a ler todas as páginas de índice restantes (indo na direção ascendente).

Como o motor de consulta pode fazer uma pesquisa binária para evitar digitalizar páginas de índice desnecessárias, os índices precisos tendem a ter latência comparável e encargos RU para indexar operações de procura.

Digitalização de índice expandida

Considere a consulta seguinte:

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

O predicado de consulta (filtragem em itens que têm sede num país que começa com casos insensíveis "United") pode ser avaliado com uma digitalização de índice expandido do headquarters/country caminho. As operações que fazem uma varredura de índice expandido têm otimizações que podem ajudar a evitar necessidades para digitalizar cada página de índice, mas são ligeiramente mais caras do que a pesquisa binária de um índice preciso.

Por exemplo, ao avaliar caso-insensível, o motor de StartsWith consulta verificará o índice para diferentes combinações possíveis de valores maiúsculas e minúsculas. Esta otimização permite que o motor de consulta evite ler a maioria das páginas de índice. Diferentes funções do sistema têm diferentes otimizações que podem usar para evitar ler cada página de índice, por isso vamos categorizá-las como uma varredura de índice expandida.

Digitalização completa do índice

Considere a consulta seguinte:

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

O predicado de consulta (filtragem em itens que têm sede num país que contém "United") pode ser avaliado com uma varredura de índice do headquarters/country caminho. Ao contrário de uma análise precisa do índice, uma análise completa do índice irá sempre analisar 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 procura do índice e a taxa de RU para os exames de índice aumentam à medida que a cardinalidade do caminho aumenta. Por outras palavras, quanto mais possível os valores distintos que o motor de consulta precisa para digitalizar, maior a latência e a carga RU envolvidas na realização de uma varredura completa do índice.

Por exemplo, considere duas propriedades: cidade e país. A cardinalidade da cidade é de 5.000 e a cardinalidade do país é de 200. Aqui estão duas consultas de exemplo que cada um tem uma função de sistema Contém que faz uma verificação completa do índice na town propriedade. A primeira consulta usará mais RUs do que a segunda consulta porque o cardinalício da cidade é maior do que o país.

    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 motor de consulta pode não ser capaz de avaliar um filtro de consulta utilizando o índice. Neste caso, o motor de consulta terá de carregar todos os itens da loja transacional para avaliar o filtro de consulta. As análises completas não utilizam o índice e têm uma carga RU que aumenta linearmente com o tamanho total dos dados. Felizmente, as operações que requerem exames completos são raras.

Consultas com expressões complexas de filtro

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

Considere a consulta seguinte:

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

Para executar esta consulta, o motor de consulta deve fazer um índice procurar headquarters/employees e analisar o índice completo em headquarters/country . O motor de consulta tem heurística interna que utiliza para avaliar a expressão do filtro de consulta o mais eficientemente possível. Neste caso, o motor de consulta evitaria a necessidade de ler páginas de índice desnecessárias fazendo o índice procurar primeiro. Se, por exemplo, apenas 50 itens correspondessem ao filtro de igualdade, o motor de consulta só precisaria de avaliar Contains nas páginas de índice que continham esses 50 itens. Um exame completo de todo o contentor não seria necessário.

Utilização do índice para funções agregados escalares

As consultas com funções agregadas devem depender exclusivamente do índice para o utilizar.

Em alguns casos, o índice pode devolver falsos positivos. Por exemplo, ao avaliar Contains o índice, o número de correspondências no índice pode exceder o número de resultados de consulta. O motor de consulta carregará todas as correspondências de índice, avaliará o filtro nos itens carregados e devolverá apenas os resultados corretos.

Para a maioria das consultas, o carregamento de falsos jogos de índice positivo não terá qualquer impacto notá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 devolver algumas partidas falsamente positivas, pelo que o motor de consulta terá de verificar se cada item carregado corresponde à expressão do filtro. Neste exemplo, o motor de consulta pode apenas precisar de carregar alguns itens extra, pelo que o impacto na utilização do índice e na carga RU é mínimo.

No entanto, as consultas com funções agregadas devem depender exclusivamente do índice para o utilizar. Por exemplo, considere a seguinte consulta com um Count agregado:

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

Como no primeiro exemplo, a função do Contains sistema pode devolver alguns falsos fósforos positivos. No entanto, ao contrário da SELECT * consulta, a Count consulta não pode avaliar a expressão do filtro nos itens carregados para verificar todos os jogos de índice. A Count consulta deve depender exclusivamente do índice, por isso, se houver uma hipótese de uma expressão de filtro devolver falsos fósforos positivos, o motor de consulta recorrerá a uma varredura completa.

As consultas com as seguintes funções agregadas devem depender exclusivamente do índice, pelo que a avaliação de algumas funções do sistema requer uma verificação completa.

Passos seguintes

Leia mais sobre a indexação nos seguintes artigos: