Partilhar via


Indexação no Cosmos DB no Microsoft Fabric

O Cosmos DB é um banco de dados sem esquema que permite fazer iterações na aplicação sem ter de lidar com a gestão de esquemas ou índices. Isto também é referido como schema na leitura, significando que o Cosmos DB não aplica um esquema aos seus dados quando estes são escritos na base de dados. Em vez disso, o seu esquema é projetado nas classes que define dentro da sua aplicação quando desserializa dados da base de dados ao ler ou consultar os seus dados.

A indexação no Cosmos DB no Microsoft Fabric foi projetada para oferecer um desempenho de consulta rápido e flexível, independentemente de como seus dados evoluem. Por padrão, o 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.

Árvore conceptual

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 possuídos 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 conceitual representa o item JSON de exemplo:

  • locations
    • 0
      • country: Germany
      • city: Berlin
    • 1
      • country: France
      • city: Paris
  • headquarters
    • country: Belgium
    • employees: 250
  • exports
    • 0
      • city: Moscow
    • 1
      • city: Athens

Diagrama da representação em árvore de um item no Cosmos DB.

O diagrama de árvore mostra um nó raiz com três ramos: "locais", "quartel-general" e "exportações". "Localizações" divide-se em dois nós numerados, cada um com dois subnós relacionados com a localização ("Alemanha ou Berlim" e "França ou Paris"). "Sede" tem "Bélgica" para sua localização e "funcionários" ("250"). As "Exportações" dividem-se em dois nós numerados, cada um com um subnó "cidade" ("Moscovo" e "Atenas").

Preste atenção em 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. Por exemplo, a primeira entrada é 0 e a segunda entrada é 1.

Caminhos da propriedade

O Cosmos DB transforma itens em árvores porque 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:

Caminho Valor
/locations/0/country "Germany"
/locations/0/city "Berlin"
/locations/1/country "France"
/locations/1/city "Paris"
/headquarters/country "Belgium"
/headquarters/employees 250
/exports/0/city "Moscow"
/exports/1/city "Athens"

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

Tipos de índice

Atualmente, o Cosmos DB suporta quatro 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. Este é o tipo de índice padrão e não precisa de ser especificado ao definir uma política de índice. O tipo de índice de intervalo é usado para:

  • Questões de igualdade:

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

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

    SELECT
      *
    FROM
      container c
    WHERE
      c.property > 0
    

    Observação

    Compatível com >, <, >=, <=, !=

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

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

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

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

    SELECT
      d
    FROM
      container c
    JOIN
      d IN c.properties
    WHERE
      d = '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.

Observação

Uma ORDER BY cláusula que ordena por uma única propriedade sempre precisa de um índice de intervalo e falha 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
      container 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.

Índice composto

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 sobre 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'
    

Desde que um predicado de filtro use um dos tipos 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.department = "Information Technology" and CONTAINS(c.team, "Pilot"):

  • Esta consulta primeiro aplica um filtro para entradas onde department = "Information Technology" usando o índice. Em seguida, ele passa todas as department = "Information Technology" entradas por um pipeline subsequente para avaliar o predicado do CONTAINS filtro.

  • 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.

Índice vetorial

Os índices vetoriais aumentam a eficiência ao realizar pesquisas vetoriais usando a função do VECTORDISTANCE sistema. As pesquisas vetoriais têm menor latência, maior taxa de transferência e menor consumo de RU ao usar um índice vetorial. O Cosmos DB suporta qualquer incorporação vetorial (texto, imagem, multimodal, etc.) com menos de 4.096 dimensões de tamanho.

  • ORDER BY Consultas de pesquisa vetorial:

    SELECT TOP 10
      *
    FROM
      container 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 score
    FROM
      container c
    ORDER BY
      VECTORDISTANCE(c.vector1, c.vector2)
    
  • Filtros de intervalo na pontuação de similaridade.

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

Importante

Políticas vetoriais e í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.

A tabela a seguir resume as diferentes maneiras como os índices são usados no Cosmos DB:

Tipo de pesquisa Descrição Exemplos comuns Cobrança baseada no uso do índice Encargos de carregamento de itens a partir do armazenamento de dados transacionais
Busca de índice Leia apenas os valores indexados necessários e carregue somente os itens correspondentes da loja de dados transacionais. Filtros de igualdade, IN Constante para 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 a uma consulta por í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 (não 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
Verificação 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 StartsWithContains 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

Sugestão

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

Considere estes dois 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 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 Valor Lista de identificadores de itens
/locations/0/country Germany [1]
/locations/0/country Ireland [2]
/locations/0/city Berlin [1]
/locations/0/city Dublin [2]
/locations/1/country France [1]
/locations/1/city Paris [1]
/headquarters/country Belgium [1, 2]
/headquarters/employees 200 [2]
/headquarters/employees 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 facilmente servir ORDER BY 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 sua região) corresponderia ao caminho descrito aqui:

  • locations
    • 1
      • country: France

Diagrama de uma travessia (pesquisa) correspondente a um caminho específico dentro de uma representação em árvore de um item no Cosmos DB.

O diagrama de árvore mostra um nó raiz com três ramos: "locais", "quartel-general" e "exportações". "Localizações" divide-se em dois nós numerados, cada um com dois subnós relacionados com a localização ("Alemanha/Berlim" e "França/Paris"). "Sede" tem "Bélgica" para sua localização e "funcionários" ("250"). As "Exportações" dividem-se em dois nós numerados, cada um com um subnó "cidade" ("Moscovo" e "Atenas"). Os caminhos para "locais", "1", "localizações" e "França" são destacados.

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 (filtrando itens com mais de 200 funcionários) pode ser avaliado com uma varredura precisa do índice no caminho headquarters/employees. Ao realizar uma verificação de índice precisa, o mecanismo de consulta começa fazendo uma pesquisa binária do conjunto distinto e possível de valores para localizar o valor 200 no caminho headquarters/employees. 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 de itens que têm sede num local cujo nome começa com "United", não diferenciando maiúsculas de minúsculas) pode ser avaliado através de um escaneamento expandido do índice no caminho headquarters/country. 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 a condição de insensibilidade a maiúsculas e minúsculas em StartsWith, o mecanismo de consulta verifica o índice para várias combinações de valores em maiúsculas e minúsculas. 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 num local que contém "United") pode ser avaliado com uma verificação de índice no caminho headquarters/country. 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 pesquisas 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 dois exemplos de consultas, cada um com uma CONTAINS função do sistema que realiza uma análise completa do índice na town propriedade. A primeira consulta usa mais unidades de solicitação (RUs) do que a segunda consulta porque a cardinalidade da cidade é maior do que country.

SELECT
  *
FROM
  container 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 análises completas não utilizam 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, essa consulta 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 esta consulta, o mecanismo de consulta deve realizar uma busca de índice em headquarters/employees e uma verificação completa de índice 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 alguns resultados de falsos positivos, portanto, o mecanismo de consulta precisa verificar se cada item lá existente corresponde à expressão de filtro. Neste exemplo, o mecanismo de consulta pode precisar apenas carregar 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 consulta COUNT deve depender exclusivamente do índice, por isso, se houver uma chance de uma expressão de filtro retornar correspondências de falsos positivos, o mecanismo de consulta fará um rastreio completo.

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.