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:
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 adicionadaORDER 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:
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: