Sugestões de desempenho para o Azure Cosmos DB e .NET

APLICA-SE A: NoSQL

O Azure Cosmos DB é um banco de dados distribuído rápido e flexível que pode ser dimensionado perfeitamente com níveis garantidos de latência e taxa de transferência. Você não precisa fazer grandes alterações na arquitetura ou escrever código complexo para dimensionar seu banco de dados com o Azure Cosmos DB. Escalar para cima e para baixo é tão fácil quanto fazer uma única chamada de API. Para saber mais, consulte Provisionar taxa de transferência de contêiner ou Provisionar taxa de transferência de banco de dados.

Como o Azure Cosmos DB é acessado por meio de chamadas de rede, você pode fazer otimizações do lado do cliente para atingir o desempenho máximo ao usar o SDK do SQL .NET.

Se você estiver tentando melhorar o desempenho do banco de dados, considere as opções apresentadas nas seções a seguir.

Recomendações de hospedagem

Ativar a coleta de lixo do lado do servidor

Reduzir a frequência da coleta de lixo pode ajudar em alguns casos. No .NET, defina gcServer como true.

Dimensione a carga de trabalho do cliente

Se você estiver testando em altos níveis de taxa de transferência ou em taxas superiores a 50.000 unidades de solicitação por segundo (RU/s), o aplicativo cliente pode se tornar um gargalo de carga de trabalho. Isso ocorre porque a máquina pode limitar a utilização da CPU ou da rede. Se você chegar a esse ponto, poderá continuar a impulsionar ainda mais a conta do Azure Cosmos DB dimensionando seus aplicativos cliente em vários servidores.

Nota

O alto uso da CPU pode causar maior latência e exceções de tempo limite de solicitação.

Operações de metadados

Não verifique se um Banco de Dados e/ou Contêiner existe chamando Create...IfNotExistsAsync e/ou Read...Async no caminho ativo e/ou antes de executar uma operação de item. A validação só deve ser feita na inicialização do aplicativo quando for necessário, se você espera que eles sejam excluídos (caso contrário, não é necessário). Essas operações de metadados gerarão latência extra de ponta a ponta, não terão SLA e suas próprias limitações separadas que não são dimensionadas como operações de dados.

Registo e rastreio

Alguns ambientes têm o .NET DefaultTraceListener habilitado. O DefaultTraceListener apresenta problemas de desempenho em ambientes de produção, causando altos gargalos de CPU e E/S. Verifique e certifique-se de que o DefaultTraceListener está desativado para seu aplicativo removendo-o do TraceListeners em ambientes de produção.

As versões mais recentes do SDK (maiores que 3.23.0) o removem automaticamente quando o detetam, com versões mais antigas, você pode removê-lo por:

if (!Debugger.IsAttached)
{
    Type defaultTrace = Type.GetType("Microsoft.Azure.Cosmos.Core.Trace.DefaultTrace,Microsoft.Azure.Cosmos.Direct");
    TraceSource traceSource = (TraceSource)defaultTrace.GetProperty("TraceSource").GetValue(null);
    traceSource.Listeners.Remove("Default");
    // Add your own trace listeners
}

Rede

Política de conexão: usar o modo de conexão direta

O modo de conexão padrão do SDK do .NET V3 é direto com o protocolo TCP. Você configura o modo de conexão ao criar a CosmosClient instância no CosmosClientOptions. Para saber mais sobre as diferentes opções de conectividade, consulte o artigo Modos de conectividade.

string connectionString = "<your-account-connection-string>";
CosmosClient client = new CosmosClient(connectionString,
new CosmosClientOptions
{
    ConnectionMode = ConnectionMode.Gateway // ConnectionMode.Direct is the default
});

Exaustão de portas efémeras

Se você vir um alto volume de conexão ou alto uso de porta em suas instâncias, primeiro verifique se as instâncias do cliente são singletons. Em outras palavras, as instâncias do cliente devem ser exclusivas durante o tempo de vida do aplicativo.

Quando está sendo executado no protocolo TCP, o cliente otimiza a latência usando as conexões de longa duração. Isso contrasta com o protocolo HTTPS, que encerra as conexões após dois minutos de inatividade.

Em cenários em que você tem acesso esparso e se notar uma contagem de conexões maior quando comparado ao acesso no modo Gateway, você pode:

Para desempenho, coloque clientes na mesma região do Azure

Quando possível, coloque todos os aplicativos que chamam o Azure Cosmos DB na mesma região que o banco de dados do Azure Cosmos DB. Aqui está uma comparação aproximada: as chamadas para o Azure Cosmos DB dentro da mesma região terminam dentro de 1 milissegundo (ms) a 2 ms, mas a latência entre a costa oeste e leste dos EUA é superior a 50 ms. Essa latência pode variar de solicitação para solicitação, dependendo da rota tomada pela solicitação à medida que ela passa do cliente para o limite do datacenter do Azure.

Você pode obter a menor latência possível garantindo que o aplicativo de chamada esteja localizado na mesma região do Azure que o ponto de extremidade provisionado do Azure Cosmos DB. Para obter uma lista de regiões disponíveis, consulte Regiões do Azure.

Coloque clientes na mesma região.

Aumentar o número de threads/tarefas

Como as chamadas para o Azure Cosmos DB são feitas pela rede, talvez seja necessário variar o grau de simultaneidade de suas solicitações para que o aplicativo cliente passe o mínimo de tempo esperando entre as solicitações. Por exemplo, se você estiver usando a Biblioteca Paralela de Tarefas do .NET, crie na ordem de centenas de tarefas que leem ou gravam no Azure Cosmos DB.

Habilite a rede acelerada para reduzir a latência e os desvios da CPU

É recomendável que você siga as instruções para habilitar a Rede Acelerada em sua VM do Azure do Windows (clique para obter instruções) ou Linux (clique para obter instruções), a fim de maximizar o desempenho.

Sem rede acelerada, a E/S que transita entre sua VM do Azure e outros recursos do Azure pode ser roteada desnecessariamente por meio de um host e comutador virtual situado entre a VM e sua placa de rede. Ter o host e o comutador virtual embutidos no caminho de dados não só aumenta a latência e os desvios no canal de comunicação, como também rouba ciclos de CPU da VM. Com rede acelerada, a VM interage diretamente com a NIC sem intermediários; quaisquer detalhes da política de rede que estavam sendo manipulados pelo host e comutador virtual agora são tratados em hardware na NIC; O host e o comutador virtual são ignorados. Geralmente, você pode esperar menor latência e maior taxa de transferência, bem como latência mais consistente e menor utilização da CPU quando você habilita a rede acelerada.

Limitações: a rede acelerada deve ser suportada no sistema operacional da VM e só pode ser habilitada quando a VM é interrompida e deslocalizada. A VM não pode ser implantada com o Azure Resource Manager. O Serviço de Aplicativo não tem rede acelerada habilitada.

Consulte as instruções para Windows e Linux para obter mais detalhes.

Utilização do SDK

Instalar o SDK mais recente

Os SDKs do Azure Cosmos DB estão sendo constantemente aprimorados para fornecer o melhor desempenho. Para determinar o SDK mais recente e revisar as melhorias, consulte SDK do Azure Cosmos DB.

Usar APIs de fluxo

O .NET SDK V3 contém APIs de fluxo que podem receber e retornar dados sem serialização.

Os aplicativos de camada intermediária que não consomem respostas diretamente do SDK, mas as retransmitem para outras camadas de aplicativo, podem se beneficiar das APIs de fluxo. Para obter exemplos de manipulação de fluxo, consulte os exemplos de gerenciamento de itens.

Usar um cliente singleton do Azure Cosmos DB durante o tempo de vida do seu aplicativo

Cada CosmosClient instância é thread-safe e executa gerenciamento de conexão eficiente e cache de endereços quando opera no modo Direct. Para permitir um gerenciamento de conexão eficiente e um melhor desempenho do cliente SDK, recomendamos que você use uma única instância por AppDomain durante o tempo de vida do aplicativo para cada conta com a qual seu aplicativo interage.

Para aplicativos multilocatários que lidam com várias contas, consulte as práticas recomendadas relacionadas.

Quando você estiver trabalhando no Azure Functions, as instâncias também devem seguir as diretrizes existentes e manter uma única instância.

Evite bloquear chamadas

O SDK do Azure Cosmos DB deve ser projetado para processar muitas solicitações simultaneamente. As APIs assíncronas permitem que um pequeno pool de threads lide com milhares de solicitações simultâneas, não aguardando o bloqueio de chamadas. Em vez de aguardar a conclusão de uma tarefa síncrona de longa duração, o thread pode trabalhar em outra solicitação.

Um problema de desempenho comum em aplicativos que usam o SDK do Azure Cosmos DB é bloquear chamadas que podem ser assíncronas. Muitas chamadas de bloqueio síncronas levam à inanição do pool de threads e a tempos de resposta degradados.

Não:

  • Bloqueie a execução assíncrona chamando Task.Wait ou Task.Result.
  • Use Task.Run para tornar uma API síncrona assíncrona.
  • Adquira bloqueios em caminhos de código comuns. O Azure Cosmos DB .NET SDK tem mais desempenho quando arquitetado para executar código em paralelo.
  • Chame Task.Run e aguarde imediatamente. ASP.NET Core já executa o código do aplicativo em threads normais do Pool de Threads, portanto, chamar Task.Run resulta apenas em agendamento extra desnecessário do Pool de Threads. Mesmo que o código agendado bloqueie um thread, Task.Run não impede isso.
  • Não use ToList() no Container.GetItemLinqQueryable<T>() qual usa o bloqueio de chamadas para drenar a consulta de forma síncrona. Use ToFeedIterator() para drenar a consulta de forma assíncrona.

Fazer:

  • Chame as APIs .NET do Azure Cosmos DB de forma assíncrona.
  • Toda a pilha de chamadas é assíncrona para se beneficiar dos padrões async /await .

Um criador de perfil, como o PerfView, pode ser usado para localizar threads frequentemente adicionados ao pool de threads. O Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start evento indica um thread adicionado ao pool de threads.

Desativar a resposta de conteúdo em operações de gravação

Para cargas de trabalho com cargas úteis de criação pesadas, defina a EnableContentResponseOnWrite opção de solicitação como false. O serviço não retornará mais o recurso criado ou atualizado para o SDK. Normalmente, como o aplicativo tem o objeto que está sendo criado, ele não precisa do serviço para retorná-lo. Os valores de cabeçalho ainda estão acessíveis, como uma taxa de solicitação. Desabilitar a resposta de conteúdo pode ajudar a melhorar o desempenho, porque o SDK não precisa mais alocar memória ou serializar o corpo da resposta. Ele também reduz o uso da largura de banda da rede para ajudar ainda mais o desempenho.

ItemRequestOptions requestOptions = new ItemRequestOptions() { EnableContentResponseOnWrite = false };
ItemResponse<Book> itemResponse = await this.container.CreateItemAsync<Book>(book, new PartitionKey(book.pk), requestOptions);
// Resource will be null
itemResponse.Resource

Habilite o Bulk para otimizar a taxa de transferência em vez da latência

Habilite o Bulk para cenários em que a carga de trabalho exija uma grande quantidade de taxa de transferência e a latência não seja tão importante. Para obter mais informações sobre como habilitar o recurso em massa e saber para quais cenários ele deve ser usado, consulte Introdução ao suporte em massa.

Aumente System.Net MaxConnections por host ao usar o modo Gateway

As solicitações do Azure Cosmos DB são feitas por HTTPS/REST quando você usa o modo Gateway. Eles estão sujeitos ao limite de conexão padrão por nome de host ou endereço IP. Talvez seja necessário definir MaxConnections como um valor mais alto (de 100 a 1.000) para que a biblioteca de cliente possa usar várias conexões simultâneas com o Azure Cosmos DB. No .NET SDK 1.8.0 e posterior, o valor padrão para ServicePointManager.DefaultConnectionLimit é 50. Para alterar o valor, você pode definir Documents.Client.ConnectionPolicy.MaxConnectionLimit como um valor mais alto.

Aumentar o número de threads/tarefas

Consulte Aumentar o número de threads/tarefas na seção Rede deste artigo.

Operações de consulta

Para operações de consulta, consulte as dicas de desempenho para consultas.

Política de indexação

Excluir os caminhos não utilizados da indexação para assegurar escritas mais rápidas

A política de indexação do Azure Cosmos DB também permite especificar quais caminhos de documento devem ser incluídos ou excluídos da indexação usando caminhos de indexação (IndexingPolicy.IncludedPaths e IndexingPolicy.ExcludedPaths).

A indexação apenas dos caminhos necessários pode melhorar o desempenho de gravação, reduzir as cobranças de RU em operações de gravação e reduzir o armazenamento de índice para cenários nos quais os padrões de consulta são conhecidos de antemão. Isso ocorre porque os custos de indexação se correlacionam diretamente com o número de caminhos exclusivos indexados. Por exemplo, o código a seguir mostra como excluir uma seção inteira dos documentos (uma subárvore) da indexação usando o curinga "*":

var containerProperties = new ContainerProperties(id: "excludedPathCollection", partitionKeyPath: "/pk" );
containerProperties.IndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/*" });
containerProperties.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/nonIndexedContent/*");
Container container = await this.cosmosDatabase.CreateContainerAsync(containerProperties);

Para obter mais informações, consulte Políticas de indexação do Azure Cosmos DB.

Débito

Meça e ajuste para menor uso de RU/s

O Azure Cosmos DB oferece um conjunto avançado de operações de banco de dados. Essas operações incluem consultas relacionais e hierárquicas com arquivos UDF (Universal Disk Format), procedimentos armazenados e gatilhos, todos operando nos documentos dentro de uma coleção de banco de dados.

Os custos associados a cada uma dessas operações variam dependendo da CPU, E/S e memória necessárias para concluir a operação. Em vez de pensar e gerenciar recursos de hardware, você pode pensar em uma Unidade de Solicitação como uma única medida para os recursos necessários para executar várias operações de banco de dados e atender a uma solicitação de aplicativo.

A taxa de transferência é provisionada com base no número de Unidades de Solicitação definidas para cada contêiner. O consumo unitário de solicitação é avaliado como uma taxa de unidades por segundo. Os aplicativos que excedem a taxa de Unidade de Solicitação provisionada para seu contêiner são limitados até que a taxa caia abaixo do nível provisionado para o contêiner. Se seu aplicativo exigir um nível mais alto de taxa de transferência, você poderá aumentar sua taxa de transferência provisionando Unidades de Solicitação adicionais.

A complexidade de uma consulta afeta a quantidade de Unidades de Pedido consumidas numa operação. O número de predicados, a natureza dos predicados, o número de arquivos UDF e o tamanho do conjunto de dados de origem influenciam o custo das operações de consulta.

Para medir a sobrecarga de qualquer operação (criar, atualizar ou excluir), inspecione o cabeçalho x-ms-request-charge (ou a propriedade equivalente RequestCharge no ResourceResponse<T> SDK do FeedResponse<T> .NET) para medir o número de Unidades de Solicitação consumidas pelas operações:

// Measure the performance (Request Units) of writes
ItemResponse<Book> response = await container.CreateItemAsync<Book>(myBook, new PartitionKey(myBook.PkValue));
Console.WriteLine("Insert of item consumed {0} request units", response.RequestCharge);
// Measure the performance (Request Units) of queries
FeedIterator<Book> queryable = container.GetItemQueryIterator<ToDoActivity>(queryString);
while (queryable.HasMoreResults)
    {
        FeedResponse<Book> queryResponse = await queryable.ExecuteNextAsync<Book>();
        Console.WriteLine("Query batch consumed {0} request units", queryResponse.RequestCharge);
    }

A cobrança de solicitação retornada neste cabeçalho é uma fração da taxa de transferência provisionada (ou seja, 2.000 RU/s). Por exemplo, se a consulta anterior retornar 1.000 documentos de 1 KB, o custo da operação será 1.000. Assim, dentro de um segundo, o servidor atende apenas duas dessas solicitações antes de limitar as solicitações posteriores. Para obter mais informações, consulte Unidades de solicitação e a calculadora de unidade de solicitação.

Lidar com limitação de taxa / taxa de solicitação muito grande

Quando um cliente tenta exceder a taxa de transferência reservada para uma conta, não há degradação de desempenho no servidor e nenhum uso da capacidade de taxa de transferência além do nível reservado. O servidor termina preventivamente a solicitação com RequestRateTooLarge (código de status HTTP 429). Ele retorna um cabeçalho x-ms-retry-after-ms que indica a quantidade de tempo, em milissegundos, que o usuário deve aguardar antes de tentar a solicitação novamente.

    HTTP Status 429,
    Status Line: RequestRateTooLarge
    x-ms-retry-after-ms :100

Todos os SDKs capturam implicitamente essa resposta, respeitam o cabeçalho retry-after especificado pelo servidor e tentam novamente a solicitação. A menos que sua conta esteja sendo acessada simultaneamente por vários clientes, a próxima tentativa será bem-sucedida.

Se você tiver mais de um cliente operando cumulativamente acima da taxa de solicitação, a contagem de tentativas padrão definida atualmente como 9 internamente pelo cliente pode não ser suficiente. Nesse caso, o cliente lança um CosmosException com o código de status 429 para o aplicativo.

Você pode alterar a contagem de repetições padrão definindo o RetryOptionsCosmosClientOptions na instância. Por padrão, o CosmosException com o código de status 429 é retornado após um tempo de espera cumulativo de 30 segundos se a solicitação continuar a operar acima da taxa de solicitação. Esse erro é retornado mesmo quando a contagem de tentativas atual é menor do que a contagem máxima de tentativas, quer o valor atual seja o padrão de 9 ou um valor definido pelo usuário.

O comportamento de repetição automatizada ajuda a melhorar a resiliência e a usabilidade para a maioria dos aplicativos. Mas pode não ser o melhor comportamento quando você está fazendo benchmarks de desempenho, especialmente quando você está medindo a latência. A latência observada pelo cliente aumentará se o experimento atingir o acelerador do servidor e fizer com que o SDK do cliente tente novamente. Para evitar picos de latência durante experimentos de desempenho, meça a carga retornada por cada operação e verifique se as solicitações estão operando abaixo da taxa de solicitação reservada.

Para obter mais informações, consulte Unidades de solicitação.

Para maior taxa de transferência, projete para documentos menores

A taxa de solicitação (ou seja, o custo de processamento da solicitação) de uma operação especificada está diretamente correlacionada ao tamanho do documento. As operações em documentos grandes custam mais do que as operações em documentos pequenos.

Próximos passos

Para obter um aplicativo de exemplo usado para avaliar o Azure Cosmos DB para cenários de alto desempenho em algumas máquinas cliente, consulte Testes de desempenho e dimensionamento com o Azure Cosmos DB.

Para saber mais sobre como projetar seu aplicativo para dimensionamento e alto desempenho, consulte Particionamento e dimensionamento no Azure Cosmos DB.