Sugestões de desempenho para o SDK Java v4 do Azure Cosmos DB

APLICA-SE A: NoSQL

Importante

As dicas de desempenho neste artigo são apenas para o SDK Java v4 do Azure Cosmos DB. Consulte as Notas de versão do SDK Java do Azure Cosmos DB v4, o repositório Maven e o guia de solução de problemas do SDK Java do Azure Cosmos DB v4 para obter mais informações. Se você estiver usando uma versão mais antiga do que a v4, consulte o guia Migrar para o SDK Java do Azure Cosmos DB v4 para obter ajuda na atualização para a v4.

O Azure Cosmos DB é um banco de dados distribuído rápido e flexível que pode ser dimensionado perfeitamente com latência e taxa de transferência garantidas. Não é necessário 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 ou chamada de método SDK. No entanto, como o Azure Cosmos DB é acessado por meio de chamadas de rede, há otimizações do lado do cliente que você pode fazer para obter o desempenho máximo ao usar o SDK Java v4 do Azure Cosmos DB.

Portanto, se você estiver perguntando "Como posso melhorar o desempenho do meu banco de dados?", considere as seguintes opções:

Rede

  • Coloque clientes na mesma região do Azure para desempenho

Quando possível, coloque todos os aplicativos que chamam o Azure Cosmos DB na mesma região do banco de dados do Azure Cosmos DB. Para uma comparação aproximada, as chamadas para o Azure Cosmos DB dentro da mesma região são concluídas dentro de 1 a 2 ms, mas a latência entre a costa oeste e leste dos EUA é >de 50 ms. Essa latência provavelmente 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. A menor latência possível é alcançada 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.

Ilustração da política de conexão do Azure Cosmos DB

Um aplicativo que interage com uma conta do Azure Cosmos DB de várias regiões precisa configurar locais preferenciais para garantir que as solicitações sejam enviadas para uma região colocada.

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

É altamente recomendável seguir as instruções para habilitar a Rede Acelerada em sua VM do Azure do Windows (selecione para obter instruções) ou Linux (selecione para obter instruções) para maximizar o desempenho reduzindo a latência e os desvios da CPU.

Sem rede acelerada, a E/S que transita entre sua VM do Azure e outros recursos do Azure pode ser roteada 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 a rede acelerada, a VM interage diretamente com a NIC sem intermediários. Todos os detalhes da política de rede são tratados no hardware da NIC, ignorando o host e o comutador virtual. 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.

Para obter mais informações, consulte as instruções do Windows e Linux .

Ajustando a configuração de conexão direta e de gateway

Para otimizar as configurações de conexão no modo direto e gateway, consulte como ajustar as configurações de conexão para o java sdk v4.

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 as melhorias mais recentes do SDK, visite o SDK do Azure Cosmos DB.

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

Cada instância de cliente do Azure Cosmos DB é thread-safe e executa gerenciamento de conexão eficiente e cache de endereço. Para permitir um gerenciamento de conexão eficiente e um melhor desempenho pelo cliente Azure Cosmos DB, é altamente recomendável usar uma única instância do cliente Azure Cosmos DB durante o tempo de vida do aplicativo.

  • Use o nível de consistência mais baixo necessário para seu aplicativo

Quando você cria um CosmosClient, a consistência padrão usada, se não definida explicitamente, é Session. Se a consistência da sessão não for exigida pela lógica do aplicativo, defina a Consistência como Eventual. Observação: recomenda-se usar pelo menos a consistência de sessão em aplicativos que empregam o processador de Feed de Alterações do Azure Cosmos DB.

  • Usar a API assíncrona para maximizar a taxa de transferência provisionada

O SDK Java v4 do Azure Cosmos DB inclui duas APIs, uma Síncrona e uma Assíncrona. Grosso modo, a API Assíncrona implementa a funcionalidade do SDK, enquanto a API de Sincronização é um wrapper fino que faz chamadas de bloqueio para a API Assíncrona. Isso contrasta com o antigo Azure Cosmos DB Async Java SDK v2, que era somente Async, e com o antigo Azure Cosmos DB Sync Java SDK v2, que era somente Sync e tinha uma implementação separada.

A escolha da API é determinada durante a inicialização do cliente; um CosmosAsyncClient suporta API Assíncrona, enquanto um CosmosClient suporta API de Sincronização.

A API Assíncrona implementa E/S sem bloqueio e é a escolha ideal se o seu objetivo for maximizar a taxa de transferência ao emitir solicitações para o Azure Cosmos DB.

Usar a API de sincronização pode ser a escolha certa se você quiser ou precisar de uma API, que bloqueia a resposta a cada solicitação, ou se a operação síncrona for o paradigma dominante em seu aplicativo. Por exemplo, você pode querer a API de sincronização quando estiver persistindo dados para o Azure Cosmos DB em um aplicativo de microsserviços, desde que a taxa de transferência não seja crítica.

Observação A taxa de transferência da API de sincronização se degrada com o aumento do tempo de resposta da solicitação, enquanto a API assíncrona pode saturar todos os recursos de largura de banda do seu hardware.

A colocação geográfica pode oferecer uma taxa de transferência maior e mais consistente ao usar a API de sincronização (consulte Colocar clientes na mesma região do Azure para obter desempenho), mas ainda não se espera que exceda a taxa de transferência alcançável da API assíncrona.

Alguns usuários também podem não estar familiarizados com o Project Reator, a estrutura Reative Streams usada para implementar a API assíncrona do SDK Java do Azure Cosmos DB v4. Se isso for uma preocupação, recomendamos que você leia nosso Guia introdutório de Padrão de Reator e, em seguida, dê uma olhada nesta Introdução à Programação Reativa para se familiarizar. Se você já usou o Azure Cosmos DB com uma interface assíncrona e o SDK usado foi o Azure Cosmos DB Async Java SDK v2, talvez esteja familiarizado com o ReactiveX/RxJava, mas não tenha certeza do que mudou no Project Reator. Nesse caso, dê uma olhada em nosso Guia Reator vs. RxJava para se familiarizar.

Os trechos de código a seguir mostram como inicializar seu cliente Azure Cosmos DB para a operação da API Assíncrona ou da API de Sincronização, respectivamente:

Java SDK V4 (Maven com.azure::azure-cosmos) API assíncrona


CosmosAsyncClient client = new CosmosClientBuilder()
        .endpoint(HOSTNAME)
        .key(MASTERKEY)
        .consistencyLevel(CONSISTENCY)
        .buildAsyncClient();

  • Dimensione a carga de trabalho do cliente

Se você estiver testando em altos níveis de taxa de transferência, o aplicativo cliente pode se tornar o gargalo devido à máquina 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.

Uma boa regra geral é não exceder >50% de utilização da CPU em qualquer servidor, para manter a latência baixa.

  • Use o Agendador Apropriado (Evite roubar threads do Event loop IO Netty)

A funcionalidade assíncrona do SDK Java do Azure Cosmos DB é baseada em E/S sem bloqueio. O SDK utiliza um número fixo de threads Netty do ciclo de eventos de E/S (como muitos núcleos da CPU que a sua máquina tem) para executar operações de E/S. O Fluxo devolvido pela API emite o resultado num dos threads Netty do ciclo de eventos de E/S partilhado. Assim, é importante não bloquear os threads Netty do ciclo de eventos de E/S partilhados. Fazer um trabalho intensivo de CPU ou bloquear a operação no thread netty do loop de eventos de E/S pode causar deadlock ou reduzir significativamente a taxa de transferência do SDK.

Por exemplo, o código a seguir executa um trabalho intensivo de CPU no thread de rede IO do loop de eventos:


Mono<CosmosItemResponse<CustomPOJO>> createItemPub = asyncContainer.createItem(item);
createItemPub.subscribe(
        itemResponse -> {
            //this is executed on eventloop IO netty thread.
            //the eventloop thread is shared and is meant to return back quickly.
            //
            // DON'T do this on eventloop IO netty thread.
            veryCpuIntensiveWork();
        });


Depois que o resultado for recebido, você deve evitar fazer qualquer trabalho intensivo de CPU no resultado no thread de IO netty do loop de eventos. Em vez disso, você pode fornecer seu próprio Agendador para fornecer seu próprio thread para executar seu trabalho, conforme mostrado abaixo (requer import reactor.core.scheduler.Schedulers).


Mono<CosmosItemResponse<CustomPOJO>> createItemPub = asyncContainer.createItem(item);
createItemPub
        .publishOn(Schedulers.parallel())
        .subscribe(
                itemResponse -> {
                    //this is now executed on reactor scheduler's parallel thread.
                    //reactor scheduler's parallel thread is meant for CPU intensive work.
                    veryCpuIntensiveWork();
                });

Com base no tipo de seu trabalho, você deve usar o Agendador de Reator existente apropriado para o seu trabalho. Leia aqui Schedulers.

Para entender melhor o modelo de threading e agendamento do projeto Reator, consulte esta postagem do blog do Project Reator.

Para obter mais informações sobre o SDK Java v4 do Azure Cosmos DB, consulte o diretório do Azure Cosmos DB do SDK do Azure para Java monorepo no GitHub.

  • Otimizar as configurações de registro em log em seu aplicativo

Por vários motivos, você deve adicionar o registro em log em um thread que está gerando alta taxa de transferência de solicitação. Se seu objetivo for saturar totalmente a taxa de transferência provisionada de um contêiner com solicitações geradas por esse thread, as otimizações de log podem melhorar muito o desempenho.

  • Configurar um registrador assíncrono

A latência de um logger síncrono necessariamente leva em conta o cálculo de latência geral do thread gerador de solicitação. Um registrador assíncrono, como o log4j2 , é recomendado para separar a sobrecarga de log dos threads de aplicativos de alto desempenho.

  • Desativar o registo da netty

O registro da biblioteca Netty é tagarela e precisa ser desativado (suprimir o sinal na configuração pode não ser suficiente) para evitar custos adicionais de CPU. Se você não estiver no modo de depuração, desative completamente o registro do netty. Portanto, se você estiver usando o Log4j para remover os custos adicionais de CPU incorridos pela org.apache.log4j.Category.callAppenders() netty, adicione a seguinte linha à sua base de código:

org.apache.log4j.Logger.getLogger("io.netty").setLevel(org.apache.log4j.Level.OFF);
  • OS Open files Limite de recursos

Alguns sistemas Linux (como o Red Hat) têm um limite superior no número de arquivos abertos e, portanto, no número total de conexões. Execute o seguinte para exibir os limites atuais:

ulimit -a

O número de arquivos abertos (nofile) precisa ser grande o suficiente para ter espaço suficiente para o tamanho do pool de conexões configurado e outros arquivos abertos pelo sistema operacional. Ele pode ser modificado para permitir um tamanho maior do pool de conexões.

Abra o arquivo limits.conf:

vim /etc/security/limits.conf

Adicione/modifique as seguintes linhas:

* - nofile 100000
  • Especificar chave de partição em gravações pontuais

Para melhorar o desempenho de gravações pontuais, especifique a chave de partição do item na chamada da API de gravação de ponto, conforme mostrado abaixo:

Java SDK V4 (Maven com.azure::azure-cosmos) API assíncrona

asyncContainer.createItem(item,new PartitionKey(pk),new CosmosItemRequestOptions()).block();

Em vez de fornecer apenas a instância do item, como mostrado abaixo:

Java SDK V4 (Maven com.azure::azure-cosmos) API assíncrona

asyncContainer.createItem(item).block();

Este último é suportado, mas adicionará latência ao seu aplicativo; o SDK deve analisar o item e extrair a chave de partição.

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 permite especificar quais caminhos de documento devem ser incluídos ou excluídos da indexação usando Caminhos de Indexação (setIncludedPaths e setExcludedPaths). O uso de caminhos de indexação pode oferecer melhor desempenho de gravação e menor armazenamento de índice para cenários nos quais os padrões de consulta são conhecidos de antemão, já que os custos de indexação estão diretamente correlacionados ao número de caminhos exclusivos indexados. Por exemplo, o código a seguir mostra como incluir e excluir seções inteiras dos documentos (também conhecida como subárvore) da indexação usando o curinga "*".


CosmosContainerProperties containerProperties = new CosmosContainerProperties(containerName, "/lastName");

// Custom indexing policy
IndexingPolicy indexingPolicy = new IndexingPolicy();
indexingPolicy.setIndexingMode(IndexingMode.CONSISTENT);

// Included paths
List<IncludedPath> includedPaths = new ArrayList<>();
includedPaths.add(new IncludedPath("/*"));
indexingPolicy.setIncludedPaths(includedPaths);

// Excluded paths
List<ExcludedPath> excludedPaths = new ArrayList<>();
excludedPaths.add(new ExcludedPath("/name/*"));
indexingPolicy.setExcludedPaths(excludedPaths);

containerProperties.setIndexingPolicy(indexingPolicy);

ThroughputProperties throughputProperties = ThroughputProperties.createManualThroughput(400);

database.createContainerIfNotExists(containerProperties, throughputProperties);
CosmosAsyncContainer containerIfNotExists = database.getContainer(containerName);

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

Débito

  • Meça e ajuste para unidades de solicitação mais baixas/segundo de uso

O Azure Cosmos DB oferece um conjunto avançado de operações de banco de dados, incluindo consultas relacionais e hierárquicas com UDFs, procedimentos armazenados e gatilhos, todos operando nos documentos de uma coleção de banco de dados. O custo associado a cada uma destas operações varia com base na 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 (RU) 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 por segundo. Os aplicativos que excedem a taxa unitária 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 quantas unidades de solicitação são consumidas para uma operação. O número de predicados, a natureza dos predicados, o número de UDFs 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 para medir o número de unidades de solicitação consumidas por essas operações. Você também pode examinar a propriedade RequestCharge equivalente em ResourceResponse<T> ou FeedResponse<T>.

Java SDK V4 (Maven com.azure::azure-cosmos) API assíncrona

CosmosItemResponse<CustomPOJO> response = asyncContainer.createItem(item).block();

response.getRequestCharge();

A cobrança de solicitação retornada neste cabeçalho é uma fração da taxa de transferência provisionada. Por exemplo, se você tiver 2000 RU/s provisionados e se a consulta anterior retornar 1.000 documentos de 1KB, o custo da operação será 1000. Como tal, dentro de um segundo, o servidor honra apenas duas dessas solicitações antes de limitar as solicitações subsequentes. Para obter mais informações, consulte Unidades de solicitação e a calculadora de unidades 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 terminará preventivamente a solicitação com RequestRateTooLarge (código de status HTTP 429) e retornará o cabeçalho x-ms-retry-after-ms indicando a quantidade de tempo, em milissegundos, que o usuário deve aguardar antes de tentar novamente a solicitação.

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 atualmente definida como 9 internamente pelo cliente pode não ser suficiente; nesse caso, o cliente lança um CosmosClientException com o código de status 429 para o aplicativo. A contagem de tentativas padrão pode ser alterada usando setMaxRetryAttemptsOnThrottledRequests() na ThrottlingRetryOptions instância. Por padrão, o CosmosClientException 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. Isso ocorre mesmo quando a contagem de tentativas atual é menor do que a contagem máxima de tentativas, seja o padrão de 9 ou um valor definido pelo usuário.

Embora o comportamento de repetição automatizada ajude a melhorar a resiliência e a usabilidade para a maioria dos aplicativos, ele pode entrar em desacordo ao fazer benchmarks de desempenho, especialmente ao medir 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.

  • Design para documentos menores para maior taxa de transferência

A taxa de solicitação (o custo de processamento da solicitação) de uma determinada operação está diretamente correlacionada com o tamanho do documento. As operações em documentos grandes custam mais do que as operações em documentos pequenos. Idealmente, arquitete seu aplicativo e fluxos de trabalho para que o tamanho do item seja de ~1 KB, ou ordem ou magnitude semelhante. Para aplicativos sensíveis à latência, itens grandes devem ser evitados - documentos de vários MB tornam seu aplicativo mais lento.

Próximos passos

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

Tentando fazer o planejamento de capacidade para uma migração para o Azure Cosmos DB? Você pode usar informações sobre seu cluster de banco de dados existente para planejamento de capacidade.