Otimizando o Desempenho no Azure Cosmos DB: Melhores Práticas e Dicas

Quando estamos trabalhando com um banco de dados, a otimização é crucial e fundamental em termos de desempenho e eficiência da aplicação. Da mesma forma, no Azure Cosmos DB, a otimização é crucial para maximizar a eficiência, minimizar os custos e garantir que sua aplicação se expanda de forma eficaz. Abaixo estão algumas das melhores práticas com exemplos de codificação para otimizar o desempenho no Azure Cosmos DB.

1. Seleção da Chave de Partição Correta

A escolha de uma chave de partição apropriada é vital para bancos de dados distribuídos como o Cosmos DB. Uma boa chave de partição garante que os dados sejam distribuídos de forma equitativa entre as partições, reduzindo pontos de sobrecarga e melhorando o desempenho.

A seleção de uma chave de partição é simples, mas muito importante no momento do design no Azure Cosmos DB. Uma vez que nós selecionamos a chave de partição, não é possível alterá-la no local.

Melhor Prática

  • Selecione uma chave de partição com alta cardinalidade (muitos valores únicos).
  • Garanta que distribua as operações de leitura e gravação de forma equitativa.
  • Mantenha os dados relacionados juntos para minimizar consultas entre partições.

Exemplo: Criando um Contêiner com uma Chave de Partição Ideal

C#

 

var database = await cosmosClient.CreateDatabaseIfNotExistsAsync("YourDatabase");
var containerProperties = new ContainerProperties
{
    Id = "myContainer",
    PartitionKeyPath = "/customerId"  // Partition key selected to ensure balanced distribution
}; 

// Crie o contêiner com uma taxa de transferência provisionada de 400 RU/s
var container = await database.CreateContainerIfNotExistsAsync(containerProperties, throughput: 400);

2. Correta Uso de Indexação

No Azure Cosmos DB, os índices são aplicados a todas as propriedades por padrão, o que pode ser benéfico, mas pode resultar em custos de armazenamento e RU/s aumentados. Para melhorar o desempenho da consulta e minimizar despesas, considere personalizar a política de indexação. Cosmos DB suporta três tipos de índices: Índices de Intervalo, Índices Espaciais e Índices Compostos. Use o tipo adequado sabiamente.

Boa Prática

  • Exclua campos desnecessários da indexação.
  • Use índices compostos para consultas com vários campos.

Exemplo: Política de Indexação Personalizada

C#

 

{
    "indexingPolicy": {
        "automatic": true,
        "indexingMode": "consistent",  // Can use 'none' or 'lazy' to reduce write costs
        "includedPaths": [
            {
                "path": "/orderDate/?",  // Only index specific fields like orderDate
                "indexes": [
                    {
                        "kind": "Range",
                        "dataType": "Number"
                    }
                ]
            }
        ],
        "excludedPaths": [
            {
                "path": "/largeDataField/*"  // Exclude large fields not used in queries
            }
        ]
    }
}

Exemplo: Adicionando um Índice Composto para Consultas Otimizadas

C#

 

{
    "indexingPolicy": {
        "compositeIndexes": [
            [
                { "path": "/lastName", "order": "ascending" },
                { "path": "/firstName", "order": "ascending" }
            ]
        ]
    }
}

Você pode ler mais sobre os tipos de indexação aqui.

3. Otimizar Consultas

A consulta eficiente é crucial para minimizar as unidades de solicitação (RU/s) e melhorar o desempenho no Azure Cosmos DB. O custo de RU/s depende da complexidade e do tamanho da consulta.

O uso de executores em massa pode reduzir ainda mais os custos, diminuindo as RUs consumidas por operação. Essa otimização ajuda a gerenciar o uso de RUs de forma eficaz e reduz suas despesas gerais no Cosmos DB.

Melhores Práticas

  • Use consultas SELECT em quantidades limitadas, recupere apenas as propriedades necessárias.
  • Evite consultas entre partições fornecendo a chave da partição em sua consulta.
  • Use filtros em campos indexados para reduzir os custos de consulta.

Exemplo: Buscar Registro de Cliente

C#

 

var query = new QueryDefinition("SELECT c.firstName, c.lastName FROM Customers c WHERE c.customerId = @customerId")
    .WithParameter("@customerId", "12345");

var iterator = container.GetItemQueryIterator<Customer>(query, requestOptions: new QueryRequestOptions
{
    PartitionKey = new PartitionKey("12345")  // Provide partition key to avoid cross-partition query
});

while (iterator.HasMoreResults)
{
    var response = await iterator.ReadNextAsync();
    foreach (var customer in response)
    {
        Console.WriteLine($"{customer.firstName} {customer.lastName}");
    }
}

4. Ajuste dos Níveis de Consistência

Os níveis de consistência definem modos operacionais específicos projetados para atender a garantias relacionadas à velocidade. Existem cinco níveis de consistência (Forte, Obsolescência Limitada, Sessão, Prefixo Consistente e Eventual) disponíveis em Cosmos DB. Cada nível de consistência impacta a latência, a disponibilidade e a taxa de transferência.

Melhores Práticas

  • Use consistência de sessão para a maioria dos cenários para equilibrar desempenho e consistência de dados.
  • A consistência forte garante a consistência dos dados, mas aumenta RU/s e latência.

Exemplo: Definindo Nível de Consistência

C#

 

var cosmosClient = new CosmosClient(
    "",
    "",
    new CosmosClientOptions
    {
        // Defina a consistência para "Sessão" para desempenho equilibrado
		ConsistencyLevel = ConsistencyLevel.Session      
});

Leia mais sobre o nível de consistência aqui.

5. Use o Throughput Provisionado (RU/s) e o Dimensionamento Automático com Sabedoria

A provisionamento de throughput é um fator-chave para alcançar eficiência de custos e desempenho ideal no Azure Cosmos DB. O serviço permite que você configure o throughput de duas maneiras:

  • RU/s Fixo: Um nível constante de Unidades de Requisição por segundo (RU/s) predefinido, adequado para cargas de trabalho com demandas de desempenho consistentes.
  • Dimensionamento Automático: Uma opção dinâmica que ajusta automaticamente o throughput com base nas flutuações de carga de trabalho, proporcionando escalabilidade e evitando superprovisionamento durante períodos de baixa atividade.

Escolher o modelo de throughput apropriado ajuda a equilibrar as necessidades de desempenho com a gestão de custos de forma eficaz.

Melhores Práticas

  • Para cargas de trabalho previsíveis, provisione o throughput manualmente.
  • Use o dimensionamento automático para cargas de trabalho imprevisíveis ou com picos.

Exemplo: Provisionando Throughput com Dimensionamento Automático

C#

 

var throughputProperties = ThroughputProperties.CreateAutoscaleThroughput(maxThroughput: 4000);  // Autoscale up to 4000 RU/s 
var container = await database.CreateContainerIfNotExistsAsync(new ContainerProperties
{
	Id = "autoscaleContainer",
	PartitionKeyPath = "/userId"
}, throughputProperties);

Exemplo: Configurando Manualmente o RU/s Fixo para Cargas de Trabalho Estáveis 

C#

 

var container = await database.CreateContainerIfNotExistsAsync(new ContainerProperties
{
    Id = "manualThroughputContainer",
    PartitionKeyPath = "/departmentId"
}, throughput: 1000);  // Fixed 1000 RU/s

6. Aproveite o Feed de Mudanças para Processamento Eficiente em Tempo Real

O feed de alterações permite o processamento em tempo real, orientado por eventos, capturando automaticamente as alterações no banco de dados, eliminando a necessidade de polling. Isso reduz a sobrecarga de consulta e aprimora a eficiência.

Melhores Práticas

  • Use o feed de alterações em cenários onde as alterações de dados em tempo real precisam ser processadas (por exemplo, análises em tempo real, notificações, alertas).

Exemplo: Lendo a partir do Feed de Alterações

C#

 

var iterator = container.GetChangeFeedIterator(
ChangeFeedStartFrom.Beginning(),
ChangeFeedMode.Incremental);
while (iterator.HasMoreResults)
{
    var changes = await iterator.ReadNextAsync();
    foreach (var change in changes)
    {
        Console.WriteLine($"Detected change: {change.Id}");
        // Processar a alteração (por exemplo, acionar evento, atualizar cache)
    }
}

7. Utilização do Tempo de Vida (TTL) para Expiração Automática de Dados

Se você tem dados que são relevantes apenas por um período limitado, como logs ou dados de sessão, habilitar o Tempo de Vida (TTL) no Azure Cosmos DB pode ajudar a gerenciar os custos de armazenamento. O TTL deleta automaticamente os dados expirados após o período de retenção especificado, eliminando a necessidade de limpeza manual de dados. Esta abordagem não apenas reduz a quantidade de dados armazenados, mas também garante que seu banco de dados seja otimizado para eficiência de custos, removendo informações obsoletas ou desnecessárias.

Melhores Práticas

  • Defina o TTL para contêineres onde os dados devem expirar automaticamente para reduzir os custos de armazenamento.

Exemplo: Definindo o Tempo de Vida (TTL) para Dados Expirados

C#

 

{
    "id": "sessionDataContainer",
    "partitionKey": { "paths": ["/sessionId"] },
    "defaultTtl": 3600  // 1 hour (3600 seconds)
}

No Cosmos DB,o valor máximo de Tempo de Vida (TTL) que pode ser definido é de 365 dias (1 ano). Isso significa que os dados podem ser automaticamente excluídos após expirarem dentro de um ano após a criação ou última modificação, dependendo de como você configura o TTL.

8. Evite Consultas entre Partições

Consultas entre partições podem aumentar significativamente o RU/s e a latência. Para evitar isso:

Melhor Prática

  • Inclua sempre a chave de partição em suas consultas.
  • Projete sua estratégia de partição para minimizar o acesso entre partições.

Exemplo: Consultando com a Chave de Partição para Evitar Consulta entre Partições

C#

 

var query = new QueryDefinition("SELECT * FROM Orders o WHERE o.customerId = @customerId")
    .WithParameter("@customerId", "12345"); 

var resultSetIterator = container.GetItemQueryIterator<Order>(query, requestOptions: new QueryRequestOptions
{
    PartitionKey = new PartitionKey("12345")
});

while (resultSetIterator.HasMoreResults)
{
    var response = await resultSetIterator.ReadNextAsync();
    foreach (var order in response)
    {
        Console.WriteLine($"Order ID: {order.Id}");
    }
}

Conclusão

Essas dicas são muito eficazes durante o desenvolvimento. Ao implementar uma estratégia de partição eficaz, personalizar políticas de indexação, otimizar consultas, ajustar níveis de consistência e selecionar os modelos de provisão de throughput apropriados, você pode melhorar muito o desempenho e a eficiência da sua implantação do Azure Cosmos DB. Essas otimizações não apenas aumentam a escalabilidade, mas também ajudam na gestão de custos enquanto proporcionam uma experiência de banco de dados de alto desempenho.

Source:
https://dzone.com/articles/optimizing-performance-in-azure-cosmos-db