Dicas de desempenho para o Azure Cosmos DB Async Java SDK v2
APLICA-SE A: NoSQL
Importante
Este não é o SDK Java mais recente para o Azure Cosmos DB! Você deve atualizar seu projeto para o Azure Cosmos DB Java SDK v4 e ler o guia de dicas de desempenho do Azure Cosmos DB Java SDK v4. Siga as instruções no guia Migrar para o Azure Cosmos DB Java SDK v4 e no guia Reator vs RxJava para atualizar.
As dicas de desempenho neste artigo são apenas para o Azure Cosmos DB Async Java SDK v2. Consulte as notas de versão do Azure Cosmos DB Async Java SDK v2, o repositório Maven e o guia de solução de problemas do Azure Cosmos DB Async Java SDK v2 para obter mais informações.
Importante
Em 31 de agosto de 2024, o Azure Cosmos DB Async Java SDK v2.x será desativado; o SDK e todos os aplicativos que usam o SDK continuarão a funcionar; O Azure Cosmos DB simplesmente deixará de fornecer mais manutenção e suporte para este SDK. Recomendamos seguir as instruções acima para migrar para o SDK Java v4 do Azure Cosmos DB.
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 atingir o desempenho máximo ao usar o Azure Cosmos DB Async Java SDK v2.
Portanto, se você estiver perguntando "Como posso melhorar o desempenho do meu banco de dados?", considere as seguintes opções:
Rede
Modo de conexão: Use o modo direto
A forma como um cliente se conecta ao Azure Cosmos DB tem implicações importantes no desempenho, especialmente em termos de latência do lado do cliente. O ConnectionMode é uma definição de configuração de chave disponível para configurar o cliente ConnectionPolicy. Para o Azure Cosmos DB Async Java SDK v2, os dois ConnectionModes disponíveis são:
O modo de gateway é suportado em todas as plataformas SDK e é a opção configurada por padrão. Se seus aplicativos são executados em uma rede corporativa com restrições rígidas de firewall, o modo Gateway é a melhor escolha, pois usa a porta HTTPS padrão e um único ponto de extremidade. A compensação de desempenho, no entanto, é que o modo Gateway envolve um salto de rede adicional toda vez que os dados são lidos ou gravados no Azure Azure Cosmos DB. Por isso, o modo Direct oferece melhor desempenho devido a menos saltos de rede.
O ConnectionMode é configurado durante a construção da instância DocumentClient com o parâmetro ConnectionPolicy.
Async Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)
public ConnectionPolicy getConnectionPolicy() {
ConnectionPolicy policy = new ConnectionPolicy();
policy.setConnectionMode(ConnectionMode.Direct);
policy.setMaxPoolSize(1000);
return policy;
}
ConnectionPolicy connectionPolicy = new ConnectionPolicy();
DocumentClient client = new DocumentClient(HOST, MASTER_KEY, connectionPolicy, null);
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.
Uso do SDK
Instalar o SDK mais recente
Os SDKs do Azure Cosmos DB estão sendo constantemente aprimorados para fornecer o melhor desempenho. Consulte as páginas Notas de versão do Azure Cosmos DB Async Java SDK v2 para determinar o SDK mais recente e revisar as melhorias.
Usar um cliente singleton do Azure Cosmos DB durante o tempo de vida do seu aplicativo
Cada instância do AsyncDocumentClient é thread-safe e executa gerenciamento eficiente de conexões e cache de endereços. Para permitir um gerenciamento de conexão eficiente e um melhor desempenho por AsyncDocumentClient, é recomendável usar uma única instância de AsyncDocumentClient por AppDomain durante o tempo de vida do aplicativo.
Ajustando ConnectionPolicy
Por padrão, as solicitações do Azure Cosmos DB no modo direto são feitas por TCP ao usar o SDK Java assíncrono do Azure Cosmos DB v2. Internamente, o SDK usa uma arquitetura especial de modo direto para gerenciar dinamicamente os recursos da rede e obter o melhor desempenho.
No SDK Java assíncrono do Azure Cosmos DB v2, o modo Direto é a melhor opção para melhorar o desempenho do banco de dados com a maioria das cargas de trabalho.
- Visão geral do modo Direto
A arquitetura do lado do cliente empregada no modo Direto permite a utilização previsível da rede e o acesso multiplexado às réplicas do Azure Cosmos DB. O diagrama acima mostra como o modo Direct roteia solicitações de cliente para réplicas no back-end do Azure Cosmos DB. A arquitetura de modo direto aloca até 10 canais no lado do cliente por réplica de banco de dados. Um canal é uma conexão TCP precedida por um buffer de solicitação, que tem 30 solicitações de profundidade. Os canais pertencentes a uma réplica são alocados dinamicamente conforme necessário pelo ponto de extremidade de serviço da réplica. Quando o usuário emite uma solicitação no modo Direct, o TransportClient roteia a solicitação para o ponto de extremidade de serviço adequado com base na chave de partição. A Fila de Solicitações armazena solicitações em buffer antes do Ponto de Extremidade de Serviço.
Opções de configuração de ConnectionPolicy para o modo Direto
Como primeiro passo, use as seguintes definições de configuração recomendadas abaixo. Entre em contato com a equipe do Azure Cosmos DB se tiver problemas neste tópico específico.
Se você estiver usando o Azure Cosmos DB como um banco de dados de referência (ou seja, o banco de dados é usado para muitas operações de leitura pontual e poucas operações de gravação), pode ser aceitável definir idleEndpointTimeout como 0 (ou seja, sem tempo limite).
Opção de configuração Predefinido bufferPageSize 8192 connectionTimeout "PT1M" idleChannelTimeout "PT0S" idleEndpointTimeout "PT1M10S" maxBufferCapacidade 8388608 maxChannelsPerEndpoint 10 maxRequestsPerChannel 30 receiveHangDetectionTime "PT1M5S" requestExpiryInterval "PT5S" requestTimeout "PT1M" requestTimerResolução "PT0.5S" sendHangDetectionTime "PT10S" shutdownTimeout "PT15S"
Dicas de programação para o modo Direct
Analise o artigo Solução de problemas do Azure Cosmos DB Async Java SDK v2 como uma linha de base para resolver quaisquer problemas do SDK.
Algumas dicas de programação importantes ao usar o modo Direct:
Use multithreading em seu aplicativo para transferência de dados TCP eficiente - Depois de fazer uma solicitação, seu aplicativo deve se inscrever para receber dados em outro thread. Não fazer isso força a operação "half-duplex" não intencional e as solicitações subsequentes são bloqueadas aguardando a resposta da solicitação anterior.
Realizar cargas de trabalho de computação intensiva em um thread dedicado - Por razões semelhantes à dica anterior, operações como processamento de dados complexos são melhor posicionadas em um thread separado. Uma solicitação que extrai dados de outro armazenamento de dados (por exemplo, se o thread utilizar armazenamentos de dados do Azure Cosmos DB e do Spark simultaneamente) pode ter maior latência e é recomendável gerar um thread adicional que aguarda uma resposta do outro armazenamento de dados.
- A E/S de rede subjacente no Azure Cosmos DB Async Java SDK v2 é gerenciada pela Netty. Veja estas dicas para evitar padrões de codificação que bloqueiam threads Netty IO.
Modelagem de dados - O SLA do Azure Cosmos DB assume que o tamanho do documento é inferior a 1 KB. A otimização do modelo de dados e da programação para favorecer um tamanho menor do documento geralmente levará à diminuição da latência. Se você precisar de armazenamento e recuperação de documentos maiores que 1 KB, a abordagem recomendada é que os documentos sejam vinculados a dados no Armazenamento de Blobs do Azure.
Ajustando consultas paralelas para coleções particionadas
O Azure Cosmos DB Async Java SDK v2 dá suporte a consultas paralelas, que permitem consultar uma coleção particionada em paralelo. Para obter mais informações, consulte exemplos de código relacionados ao trabalho com os SDKs. As consultas paralelas são projetadas para melhorar a latência e a taxa de transferência da consulta em relação à sua contraparte serial.
Conjunto de ajusteMaxDegreeOfParallelism:
As consultas paralelas funcionam consultando várias partições em paralelo. No entanto, os dados de uma coleção particionada individual são obtidos em série em relação à consulta. Portanto, use setMaxDegreeOfParallelism para definir o número de partições que tem a chance máxima de alcançar a consulta de maior desempenho, desde que todas as outras condições do sistema permaneçam as mesmas. Se você não sabe o número de partições, você pode usar setMaxDegreeOfParallelism para definir um número alto, e o sistema escolhe o mínimo (número de partições, entrada fornecida pelo usuário) como o grau máximo de paralelismo.
É importante notar que as consultas paralelas produzem os melhores benefícios se os dados forem distribuídos uniformemente em todas as partições em relação à consulta. Se a coleção particionada for particionada de tal forma que todos ou a maioria dos dados retornados por uma consulta estejam concentrados em algumas partições (uma partição na pior das hipóteses), o desempenho da consulta será afunilizado por essas partições.
Ajustando setMaxBufferedItemCount:
A consulta paralela é projetada para pré-buscar resultados enquanto o lote atual de resultados está sendo processado pelo cliente. A pré-busca ajuda na melhoria geral da latência de uma consulta. setMaxBufferedItemCount limita o número de resultados pré-buscados. Definir setMaxBufferedItemCount para o número esperado de resultados retornados (ou um número maior) permite que a consulta receba o máximo benefício da pré-busca.
A pré-busca funciona da mesma forma, independentemente do MaxDegreeOfParallelism, e há um único buffer para os dados de todas as partições.
Implementar backoff em intervalos getRetryAfterInMilliseconds
Durante o teste de desempenho, você deve aumentar a carga até que uma pequena taxa de solicitações seja limitada. Se limitado, o aplicativo cliente deve recuar para o intervalo de repetição especificado pelo servidor. Respeitar o backoff garante que você passe uma quantidade mínima de tempo esperando entre as tentativas.
Dimensione a carga de trabalho do cliente
Se você estiver testando em altos níveis de taxa de transferência (>50.000 RU/s), 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.
Usar endereçamento baseado em nome
Use endereçamento baseado em nome, onde os links têm o formato
dbs/MyDatabaseId/colls/MyCollectionId/docs/MyDocumentId
, em vez de SelfLinks (_self), que têm o formatodbs/<database_rid>/colls/<collection_rid>/docs/<document_rid>
para evitar recuperar ResourceIds de todos os recursos usados para construir o link. Além disso, como esses recursos são recriados (possivelmente com o mesmo nome), armazená-los em cache pode não ajudar.Ajuste o tamanho da página para consultas/feeds de leitura para um melhor desempenho
Ao executar uma leitura em massa de documentos usando a funcionalidade de feed de leitura (por exemplo, readDocuments) ou ao emitir uma consulta SQL, os resultados são retornados de forma segmentada se o conjunto de resultados for muito grande. Por padrão, os resultados são retornados em partes de 100 itens ou 1 MB, o limite atingido primeiro.
Para reduzir o número de viagens de ida e volta de rede necessárias para recuperar todos os resultados aplicáveis, você pode aumentar o tamanho da página usando o cabeçalho de solicitação x-ms-max-item-count para até 1000. Nos casos em que você precisa exibir apenas alguns resultados, por exemplo, se a interface do usuário ou a API do aplicativo retornar apenas 10 resultados por vez, você também poderá diminuir o tamanho da página para 10 para reduzir a taxa de transferência consumida para leituras e consultas.
Você também pode definir o tamanho da página usando o método setMaxItemCount.
Use o Agendador Apropriado (Evite roubar threads do Event loop IO Netty)
O Azure Cosmos DB Async Java SDK v2 usa netty para 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 Observable retornado pela API emite o resultado em um dos threads netty de loop de eventos de E/S compartilhados. 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:
Async Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)
Observable<ResourceResponse<Document>> createDocObs = asyncDocumentClient.createDocument( collectionLink, document, null, true); createDocObs.subscribe( resourceResponse -> { //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, se você quiser fazer um trabalho intensivo de CPU no resultado, você deve evitar fazê-lo 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.
Async Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)
import rx.schedulers; Observable<ResourceResponse<Document>> createDocObs = asyncDocumentClient.createDocument( collectionLink, document, null, true); createDocObs.subscribeOn(Schedulers.computation()) subscribe( resourceResponse -> { // this is executed on threads provided by Scheduler.computation() // Schedulers.computation() should be used only when: // 1. The work is cpu intensive // 2. You are not doing blocking IO, thread sleep, etc. in this thread against other resources. veryCpuIntensiveWork(); });
Com base no tipo de seu trabalho, você deve usar o RxJava Scheduler existente apropriado para o seu trabalho. Leia aqui
Schedulers
.Para obter mais informações, consulte a página do GitHub para o Azure Cosmos DB Async Java SDK v2.
Desativar o registo da netty
O registro da biblioteca Netty é tagarela e precisa ser desativado (suprimir o login na configuração pode não ser suficiente) para evitar custos adicionais da CPU. Se você não estiver no modo de depuração, desative completamente o registro do netty. Então, se você estiver usando log4j para remover os custos adicionais de CPU incorridos pelo
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
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 excluir uma seção inteira dos documentos (também conhecida como subárvore) da indexação usando o curinga "*".
Async Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)
Index numberIndex = Index.Range(DataType.Number); numberIndex.set("precision", -1); indexes.add(numberIndex); includedPath.setIndexes(indexes); includedPaths.add(includedPath); indexingPolicy.setIncludedPaths(includedPaths); collectionDefinition.setIndexingPolicy(indexingPolicy);
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>.
Async Java SDK V2 (Maven com.microsoft.azure::azure-cosmosdb)
ResourceResponse<Document> response = asyncClient.createDocument(collectionLink, documentDefinition, null, false).toBlocking.single(); 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 1 KB, 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 DocumentClientException com o código de status 429 para o aplicativo. A contagem de tentativas padrão pode ser alterada usando setRetryOptions na instância ConnectionPolicy. Por padrão, o DocumentClientException 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.
Próximos passos
Para saber mais sobre como projetar seu aplicativo para dimensionamento e alto desempenho, consulte Particionamento e dimensionamento no Azure Cosmos DB.