Políticas de indexação no Azure Cosmos DB

APLICA-SE A: NoSQL

No Azure Cosmos DB, cada contêiner tem uma política de indexação que determina como os itens do contêiner devem ser indexados. A política de indexação padrão para contêineres recém-criados indexa cada propriedade de cada item e impõe índices de intervalo para qualquer cadeia de caracteres ou número. Isso permite que você obtenha um bom desempenho de consulta sem precisar pensar na indexação e no gerenciamento de índice antecipadamente.

Em algumas situações, talvez você queira substituir esse comportamento automático para atender melhor às suas necessidades. Você pode personalizar a política de indexação de um contêiner configurando o modo de indexação e incluindo ou excluindo os caminhos de propriedade.

Observação

O método de atualização das políticas de indexação descritos neste artigo se aplica somente à API do Azure Cosmos DB para NoSQL. Saiba mais sobre a indexação em API do Azure Cosmos DB para MongoDB

Modo de indexação

O Azure Cosmos DB dá suporte a dois modos de indexação:

  • Consistente: o índice é atualizado de maneira síncrona à medida que você cria, atualiza ou exclui itens. Isso significa que a consistência das suas consultas de leitura será a consistência configurada da conta.
  • Nenhum: a indexação está desabilitada no contêiner. Esse modo normalmente é usada quando um contêiner funciona como um repositório de chave-valor puro sem a necessidade de índices secundários. Ela também pode ser usada para aprimorar o desempenho de operações em massa. Depois que as operações em massa forem concluídas, o modo de índice poderá ser definido como Consistente e monitorado usando o IndexTransformationProgress até a conclusão.

Observação

O Azure Cosmos DB também dá suporte a um modo de Indexação lenta. A indexação lenta faz atualizações no índice em um nível de prioridade muito menor quando o mecanismo não executa outros trabalhos. Isso pode resultar em resultados de consultas inconsistentes ou incompletas. Se você planeja consultar um contêiner do Azure Cosmos DB, não selecione a indexação lenta. Os novos contêineres não podem selecionar a indexação lenta. Você pode solicitar uma isenção entrando em contato com o cosmosdbindexing@microsoft.com (a não ser que você esteja usando uma conta do Azure Cosmos DB no modo sem servidor que não dá suporte à indexação lenta).

Por padrão, a política de indexação está definida como automatic. Ela é obtida configurando a propriedade automatic na política de indexação como true. Configurar essa propriedade como true permite que o Azure Cosmos DB indexe automaticamente os documentos conforme eles são gravados.

Tamanho do índice

No Azure Cosmos DB, o armazenamento consumido total é a combinação do Tamanho dos dados e do Tamanho do índice. Veja abaixo alguns recursos de tamanho do índice:

  • O tamanho do índice depende da política de indexação. Se todas as propriedades forem indexadas, o tamanho do índice poderá ser maior do que o tamanho dos dados.
  • Quando os dados forem excluídos, os índices serão compactados quase continuamente. No entanto, em exclusões de dados pequenas, talvez você não observe imediatamente uma diminuição no tamanho do índice.
  • O tamanho do Índice poderá aumentar temporariamente quando as partições físicas forem divididas. O espaço do índice é lançado após a conclusão da divisão da partição.

Inclusão e exclusão dos caminhos de propriedade

Uma política de indexação personalizada pode especificar caminhos de propriedade que são explicitamente incluídos ou excluídos da indexação. Ao otimizar o número de caminhos que são indexados, você pode reduzir substancialmente a latência e preço da RU das operações de gravação. Esses caminhos são definidos seguindo o método descrito na seção visão geral da indexação com as seguintes adições:

  • um caminho que leva a um valor escalar (cadeia de caracteres ou número) termina com /?
  • os elementos de uma matriz são administrados em conjunto por meio da notação /[] (em vez de /0, /1 etc.)
  • o curinga /* pode ser usado para corresponder qualquer elemento abaixo do nó

Usando o mesmo exemplo novamente:

    {
        "locations": [
            { "country": "Germany", "city": "Berlin" },
            { "country": "France", "city": "Paris" }
        ],
        "headquarters": { "country": "Belgium", "employees": 250 },
        "exports": [
            { "city": "Moscow" },
            { "city": "Athens" }
        ]
    }
  • o caminho employees do headquarters é /headquarters/employees/?

  • o caminho country do locations é /locations/[]/country/?

  • o caminho para tudo em headquarters é /headquarters/*

Por exemplo, poderíamos incluir o caminho /headquarters/employees/?. Esse caminho garantiria a indexação da propriedade employees, mas não indexaria o JSON aninhado adicional dentro dessa propriedade.

Estratégia de inclusão/exclusão

Qualquer política de indexação deve incluir o caminho raiz /* como um caminho incluído ou excluído.

  • Inclua o caminho raiz para excluir seletivamente os caminhos que não precisam ser indexados. Essa abordagem é recomendada, pois permite que Azure Cosmos DB indexe proativamente todas as novas propriedades que forem adicionadas ao seu modelo.

  • Exclua o caminho raiz para incluir seletivamente os caminhos que precisam ser indexados. O caminho da propriedade da chave de partição não é indexado por padrão com a estratégia de exclusão e deve ser incluído explicitamente, se necessário.

  • Em caminhos com caracteres regulares que incluem caracteres alfanuméricos e _ (sublinhado), você não precisa escapar a cadeia de caracteres do caminho com aspas duplas (por exemplo, "/path/?"). Em caminhos com outros caracteres especiais, você precisa escapar a cadeia de caracteres do caminho com aspas duplas (por exemplo, "/"path-abc"/?"). Se você acredita que terá caracteres especiais no seu caminho, pode escapar todos os caminhos por precaução. Funcionalmente, não faz diferença se você escapar todos os caminhos ou apenas aqueles que têm caracteres especiais.

  • A propriedade do sistema _etag é excluída da indexação por padrão, a menos que a etag seja adicionada ao caminho incluído para indexação.

  • Se o modo de indexação estiver definido como consistente, as propriedades do sistema id e _ts serão indexadas automaticamente.

  • Se um caminho explicitamente indexado não existir em um item, um valor será adicionado ao índice para indicar que o caminho é indefinido.

Todos os caminhos explicitamente incluídos terão valores adicionados ao índice para cada item no contêiner, mesmo que o caminho seja indefinido para um determinado item.

Confira esta seção para obter exemplos de política de indexação para incluir e excluir caminhos.

Precedência de inclusão/exclusão

Se os caminhos incluídos e os caminhos excluídos tiverem um conflito, o caminho mais preciso terá precedência.

Veja um exemplo:

Caminho incluído: /food/ingredients/nutrition/*

Caminho excluído: /food/ingredients/*

Nesse caso, o caminho incluído tem precedência sobre o caminho excluído porque ele é mais preciso. Com base nesses caminhos, todos os dados no caminho food/ingredients ou aninhados nele serão excluídos do índice. Os dados no caminho incluído /food/ingredients/nutrition/* são a exceção, pois eles seriam indexados.

Veja abaixo algumas regras para precedência de caminhos incluídos e excluídos no Azure Cosmos DB:

  • Caminhos mais profundos são mais precisos do que caminhos mais estreitos. por exemplo: /a/b/? é mais preciso do que /a/?.

  • /? é mais preciso do que /*. Por exemplo, /a/? é mais preciso do que /a/*, portanto /a/? tem precedência.

  • O caminho /* precisa ser um caminho incluído ou um caminho excluído.

Índices espaciais

Ao definir um caminho espacial na política de indexação, você deve definir qual type de índice deve ser aplicado a esse caminho. Os tipos possíveis para índices espaciais incluem:

  • Point

  • Polygon

  • MultiPolygon

  • LineString

O Azure Cosmos DB, por padrão, não criará nenhum índice espacial. Se você quiser usar funções internas de SQL espaciais, deverá criar um índice espacial nas propriedades requeridas. Confira esta seção para obter exemplos na política de indexação de adição de índices espaciais.

Índices compostos

As consultas que têm uma cláusula ORDER BY com duas ou mais propriedades exigem um índice composto. Você também pode definir um índice composto para aprimorar o desempenho de várias consultas de igualdade e de intervalo. Por padrão, nenhum índice composto é definido, portanto, você deve adicionar índices compostos conforme necessário.

Ao contrário dos caminhos incluídos ou excluídos, você não pode criar um caminho com o curinga /*. Cada caminho composto tem um /? implícito no final do caminho que você não precisa especificar. Os caminhos compostos levam a um valor escalar que é o único valor incluído no índice composto. Se um caminho em um índice composto não existir em um item ou levar a um valor não escalar, um valor será adicionado ao índice para indicar que o caminho está indefinido.

Ao definir um índice composto, você especifica:

  • Dois ou mais caminhos de propriedade. A sequência na qual os caminhos de propriedade são definidos é importante.

  • A ordem (crescente ou decrescente).

Observação

Quando você adiciona um índice composto, a consulta utiliza índices de intervalo existentes até que a nova adição de índice composto seja concluída. Portanto, ao adicionar um índice composto, talvez você não observe imediatamente aprimoramentos no desempenho. É possível acompanhar o progresso da transformação de índice usando um dos SDKs.

Consultas ORDER BY em várias propriedades:

As seguintes considerações são utilizadas ao usar índices compostos para consultas com uma cláusula ORDER BY com duas ou mais propriedades:

  • Se os caminhos do índice composto não corresponderem à sequência das propriedades na cláusula ORDER BY, o índice composto não poderá dar suporte à consulta.

  • A ordem dos caminhos de índice composto (crescente ou decrescente) também deve corresponder ao order na cláusula ORDER BY.

  • O índice composto também dá suporte a uma cláusula ORDER BY com a ordem oposta em todos os caminhos.

Considere o seguinte exemplo em que um índice composto é definido nas propriedades name, age e _ts:

Índices compostos Consulta ORDER BY de exemplo Compatível com índice composto?
(name ASC, age ASC) SELECT * FROM c ORDER BY c.name ASC, c.age asc Yes
(name ASC, age ASC) SELECT * FROM c ORDER BY c.age ASC, c.name asc No
(name ASC, age ASC) SELECT * FROM c ORDER BY c.name DESC, c.age DESC Yes
(name ASC, age ASC) SELECT * FROM c ORDER BY c.name ASC, c.age DESC No
(name ASC, age ASC, timestamp ASC) SELECT * FROM c ORDER BY c.name ASC, c.age ASC, timestamp ASC Yes
(name ASC, age ASC, timestamp ASC) SELECT * FROM c ORDER BY c.name ASC, c.age ASC No

Você deve personalizar a política de indexação para que ela possa atender a todas as consultas ORDER BY necessárias.

Consultas com filtros em várias propriedades

Se uma consulta tiver filtros em duas ou mais propriedades, poderá ser útil criar um índice composto para essas propriedades.

Por exemplo, veja a seguinte consulta que tem um filtro de igualdade e de intervalo:

SELECT *
FROM c
WHERE c.name = "John" AND c.age > 18

Essa consulta será mais eficiente, levando menos tempo e consumindo menos RUs, se conseguir aproveitar um índice composto em (name ASC, age ASC).

Consultas com vários filtros de intervalo também podem ser otimizadas com um índice composto. No entanto, cada índice composto individual só pode otimizar um filtro de intervalo. Os filtros de intervalo incluem >, <, <=, >= e !=. O filtro de intervalo deve ser definido por último no índice composto.

Veja a seguinte consulta com um filtro de igualdade e dois filtros de intervalo:

SELECT *
FROM c
WHERE c.name = "John" AND c.age > 18 AND c._ts > 1612212188

Essa consulta será mais eficiente com um índice composto em (name ASC, age ASC) e (name ASC, _ts ASC). No entanto, a consulta não utilizará um índice composto em (age ASC, name ASC) porque as propriedades com filtros de igualdade precisam ser definidas primeiro no índice composto. Dois índices compostos separados são necessários em vez de apenas um índice composto em (name ASC, age ASC, _ts ASC), pois cada índice composto só pode otimizar um filtro de intervalo.

As seguintes considerações são usadas ao criar índices compostos para consultas com filtros em várias propriedades

  • As expressões de filtro podem usar vários índices compostos.
  • As propriedades no filtro da consulta devem corresponder às do índice composto. Se uma propriedade estiver no índice composto, mas não for incluída na consulta como um filtro, a consulta não usará o índice composto.
  • Se uma consulta tiver outras propriedades no filtro que não foram definidas em um índice composto, uma combinação de índices compostos e de intervalo será usada para avaliar a consulta. Isso exigirá menos RUs do que usar exclusivamente índices de intervalo.
  • Se uma propriedade tiver um filtro de intervalo (>, <, <=, >= ou !=), essa propriedade deverá ser definida por último no índice composto. Se uma consulta tiver mais de um filtro de intervalo, ela poderá se beneficiar de vários índices compostos.
  • Ao criar um índice composto para otimizar consultas com vários filtros, o ORDER do índice composto não terá impacto sobre os resultados. Essa propriedade é opcional.

Considere o seguinte exemplo em que um índice composto é definido nas propriedades name, age e timestamp:

Índices compostos Exemplo de consulta Compatível com índice composto?
(name ASC, age ASC) SELECT * FROM c WHERE c.name = "John" AND c.age = 18 Yes
(name ASC, age ASC) SELECT * FROM c WHERE c.name = "John" AND c.age > 18 Yes
(name ASC, age ASC) SELECT COUNT(1) FROM c WHERE c.name = "John" AND c.age > 18 Yes
(name DESC, age ASC) SELECT * FROM c WHERE c.name = "John" AND c.age > 18 Yes
(name ASC, age ASC) SELECT * FROM c WHERE c.name != "John" AND c.age > 18 No
(name ASC, age ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" AND c.age = 18 AND c.timestamp > 123049923 Yes
(name ASC, age ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" AND c.age < 18 AND c.timestamp = 123049923 No
(name ASC, age ASC) and (name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" AND c.age < 18 AND c.timestamp > 123049923 Yes

Consultas com um filtro e ORDER BY

Se uma consulta filtrar uma ou mais propriedades e tiver propriedades diferentes na cláusula ORDER BY, poderá ser útil adicionar as propriedades no filtro à cláusula ORDER BY.

Por exemplo, ao adicionar as propriedades no filtro à cláusula ORDER BY, a seguinte consulta pode ser reescrita para aproveitar um índice composto:

Consulta que usa o índice de intervalo:

SELECT *
FROM c 
WHERE c.name = "John" 
ORDER BY c.timestamp

Consulta que usa o índice composto:

SELECT * 
FROM c 
WHERE c.name = "John"
ORDER BY c.name, c.timestamp

As mesmas otimizações de consulta podem ser generalizadas para qualquer consulta ORDER BY com filtros, tendo em mente que índices compostos individuais só podem dar suporte a, no máximo, um filtro de intervalo.

Consulta que usa o índice de intervalo:

SELECT * 
FROM c 
WHERE c.name = "John" AND c.age = 18 AND c.timestamp > 1611947901 
ORDER BY c.timestamp

Consulta que usa o índice composto:

SELECT * 
FROM c 
WHERE c.name = "John" AND c.age = 18 AND c.timestamp > 1611947901 
ORDER BY c.name, c.age, c.timestamp

Além disso, você pode usar índices compostos para otimizar consultas com funções do sistema e ORDER BY:

Consulta que usa o índice de intervalo:

SELECT * 
FROM c 
WHERE c.firstName = "John" AND Contains(c.lastName, "Smith", true) 
ORDER BY c.lastName

Consulta que usa o índice composto:

SELECT * 
FROM c 
WHERE c.firstName = "John" AND Contains(c.lastName, "Smith", true) 
ORDER BY c.firstName, c.lastName

As seguintes considerações se aplicam ao criar índices compostos para otimizar uma consulta com um filtro e uma cláusula ORDER BY:

  • Se você não definir um índice composto em uma consulta com um filtro em uma propriedade e uma cláusula ORDER BY separada usando uma propriedade diferente, a consulta ainda terá sucesso. No entanto, o custo de RU da consulta pode ser reduzido com um índice composto, especialmente se a propriedade na cláusula ORDER BY tiver uma cardinalidade alta.
  • Se a consulta filtrar propriedades, essas propriedades deverão ser incluídas primeiro na cláusula ORDER BY.
  • Se a consulta filtra várias propriedades, os filtros de igualdade precisam ser as primeiras propriedades na cláusula ORDER BY.
  • Se a consulta filtrar várias propriedades, você poderá ter no máximo um filtro de intervalo ou uma função de sistema utilizada por índice composto. A propriedade usada no filtro de intervalo ou na função do sistema deve ser definida por último no índice composto.
  • Todas as considerações para a criação de índices compostos em consultas ORDER BY com várias propriedades, bem como em consultas com filtros em várias propriedades ainda são aplicáveis.
Índices compostos Consulta ORDER BY de exemplo Compatível com índice composto?
(name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" ORDER BY c.name ASC, c.timestamp ASC Yes
(name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" AND c.timestamp > 1589840355 ORDER BY c.name ASC, c.timestamp ASC Yes
(timestamp ASC, name ASC) SELECT * FROM c WHERE c.timestamp > 1589840355 AND c.name = "John" ORDER BY c.timestamp ASC, c.name ASC No
(name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" ORDER BY c.timestamp ASC, c.name ASC No
(name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "John" ORDER BY c.timestamp ASC No
(age ASC, name ASC, timestamp ASC) SELECT * FROM c WHERE c.age = 18 and c.name = "John" ORDER BY c.age ASC, c.name ASC,c.timestamp ASC Yes
(age ASC, name ASC, timestamp ASC) SELECT * FROM c WHERE c.age = 18 and c.name = "John" ORDER BY c.timestamp ASC No

Consultas com um filtro e uma agregação

Se uma consulta filtrar uma ou mais propriedades e tiver uma função de sistema de agregação, poderá ser útil criar um índice composto para as propriedades na função do sistema de filtro e agregação. Essa otimização se aplica às funções do sistema SUM e AVG.

As considerações a seguir se aplicam ao criar índices compostos para otimizar uma consulta com um filtro e uma função do sistema de agregação.

  • Os índices compostos são opcionais ao executar consultas com agregações. No entanto, o custo de RU da consulta geralmente pode ser reduzido significativamente com um índice composto.
  • Se a consulta filtra várias propriedades, os filtros de igualdade precisam ser as primeiras propriedades no índice composto.
  • Você pode ter no máximo um filtro de intervalo por índice composto e ele precisa estar na propriedade na função do sistema de agregação.
  • A propriedade na função do sistema de agregação deve ser definida por último no índice composto.
  • A order (ASC ou DESC) não importa.
Índices compostos Exemplo de consulta Compatível com índice composto?
(name ASC, timestamp ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name = "John" Yes
(timestamp ASC, name ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name = "John" No
(name ASC, timestamp ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name > "John" No
(name ASC, age ASC, timestamp ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name = "John" AND c.age = 25 Yes
(age ASC, timestamp ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name = "John" AND c.age > 25 No

Modificando a política de indexação

A política de indexação de um contêiner pode ser atualizada a qualquer momento usando o portal do Azure ou um dos SDKs com suporte. Uma atualização para a política de indexação dispara uma transformação do índice antigo para o novo, que é executada online e in-loco (portanto, nenhum espaço de armazenamento adicional é consumido durante a operação). A política de indexação antiga é transformada com eficiência na nova política, sem afetar a disponibilidade de gravação, de leitura e nem a taxa de transferência provisionada no contêiner. A transformação de índice é uma operação assíncrona e o tempo necessário para conclui-la depende da taxa de transferência provisionada, do número de itens e do tamanho deles. Se várias atualizações de política de indexação precisarem ser feitas, é recomendável fazer todas as alterações como uma única operação para que a transformação de índice seja concluída o mais rápido possível.

Importante

A transformação de índice é uma operação que consome Unidades de Solicitação. Atualmente, as Unidades de Solicitação consumidas por uma transformação de índice não serão cobradas se você estiver usando contêineres sem servidor. Essas Unidades de Solicitação serão cobradas quando a opção sem servidor estiver em disponibilidade geral.

Observação

Você pode acompanhar o progresso da transformação de índice no portal do Azure ou usando um dos SDKs.

Não há nenhum impacto na disponibilidade de gravação durante as transformações de índice. A transformação do índice usa as RUs provisionadas, mas com uma prioridade mais baixa do que as suas consultas ou operações CRUD.

Não há nenhum impacto na disponibilidade de leitura ao adicionar novos caminhos de índice. As consultas só utilizarão novos caminhos de índice depois que a transformação do índice for concluída. Em outras palavras, ao adicionar um novo caminho indexado, as consultas que se beneficiam desse caminho indexado terão o mesmo desempenho antes e durante a transformação do índice. Após a conclusão da transformação do índice, o mecanismo de consulta começará a usar os novos caminhos indexados.

Ao remover caminhos indexados, você deve agrupar todas as alterações em uma transformação de política de indexação. Se você remover vários índices e fizer isso em apenas uma alteração de política de indexação, o mecanismo de consulta fornecerá resultados consistentes e completos em toda a transformação de índice. No entanto, se você remover índices por meio de várias alterações de política de indexação, o mecanismo de consulta não fornecerá resultados consistentes nem completos até que todas as transformações de índice sejam concluídas. A maioria dos desenvolvedores não remove índices e tenta imediatamente executar consultas que utilizam esses índices, portanto, na prática, essa situação é improvável.

Quando você remove um caminho indexado, o mecanismo de consulta para imediatamente de usá-lo, e fará um exame completo em vez disso.

Observação

Quando possível, você sempre deve tentar agrupar várias remoções de índice em uma única modificação de política de indexação.

Importante

A remoção de um índice entra em vigor imediatamente, enquanto a adição de um novo índice leva algum tempo, pois requer uma transformação de indexação. Ao substituir um índice por outro (por exemplo, substituindo um único índice de propriedades por um índice composto), adicione o novo índice primeiro e aguarde até que a transformação de índice seja concluída antes de remover o índice anterior da política de indexação. Caso contrário, isso afetará negativamente sua capacidade de consultar o índice anterior e poderá interromper todas as cargas de trabalho ativas que o referenciam.

Indexação de políticas e TTL

O uso do recurso TTL (vida útil) requer indexação. Isso significa que:

  • não é possível ativar o TTL em um contêiner no qual o modo de indexação está definido como none,
  • não é possível definir o modo de indexação como Nenhum em um contêiner no qual o TTL está ativado.

Em cenários em que nenhum caminho de propriedade precisa ser indexado, mas o TTL é necessário, você pode usar uma política de indexação com um modo de indexação definido como consistent, sem caminhos incluídos, e /* como o único caminho excluído.

Próximas etapas

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