Ajuste do desempenho para uploads e downloads com .NET

Quando um aplicativo transfere dados usando a biblioteca de clientes do Armazenamento do Azure para .NET, há vários fatores que podem afetar a velocidade, o uso de memória e até mesmo o êxito ou a falha da solicitação. Para maximizar o desempenho e a confiabilidade das transferências de dados, é importante ser proativo na configuração das opções de transferência da biblioteca do cliente com base no ambiente em que seu aplicativo é executado.

Este artigo explica várias considerações para ajustar as opções de transferência de dados e as diretrizes se aplicam a qualquer API que aceite StorageTransferOptions como parâmetro. Quando ajustada corretamente, a biblioteca de clientes pode distribuir dados com eficiência entre várias solicitações, o que pode resultar em maior velocidade de operação, uso de memória e estabilidade de rede.

Ajuste de desempenho com StorageTransferOptions

Ajustar corretamente os valores em StorageTransferOptions é fundamental para um desempenho confiável das operações de transferência de dados. As transferências de armazenamento são particionadas em várias subtransferências com base nos valores de propriedade definidos em uma instância desse struct. O tamanho máximo de transferência com suporte varia de acordo com a operação e a versão do serviço. Portanto, verifique a documentação para determinar os limites. Para obter mais informações sobre limites de tamanho de transferência para o Armazenamento de Blobs, confira Dimensionar destinos para armazenamento de Blobs.

As seguintes propriedades de StorageTransferOptions podem ser ajustadas com base nas necessidades do seu aplicativo:

Observação

Embora o struct StorageTransferOptions contenha valores anuláveis, as bibliotecas de cliente usarão padrões para cada valor individual, se não fornecido. Esses padrões normalmente têm um bom desempenho em um ambiente de data center, mas provavelmente não são adequados para ambientes de consumidor doméstico. O StorageTransferOptions ajustado de forma incorreta pode resultar em operações excessivamente longas e até mesmo tempos limite de solicitação. É melhor ser proativo ao testar os valores em StorageTransferOptions e ajustá-los com base nas necessidades do seu aplicativo e ambiente.

InitialTransferSize

InitialTransferSize é o tamanho da primeira solicitação de intervalo em bytes. Uma solicitação de intervalo HTTP é uma solicitação parcial e, nesse caso, com o tamanho é definido por InitialTransferSize. Blobs menores que esse tamanho são transferidos em uma única solicitação. Blobs maiores que esse tamanho continuam sendo transferidos em pedaços de tamanho MaximumTransferSize.

É importante observar que o valor especificado para MaximumTransferSize não limita o valor definido para InitialTransferSize. InitialTransferSize define uma limitação de tamanho separada para uma solicitação inicial, para executar toda a operação de uma só vez, sem subtransferências. Muitas vezes, você deseja que InitialTransferSize seja pelo menos tão grande quanto o valor definido para MaximumTransferSize, se não maior. Dependendo do tamanho da transferência de dados, essa abordagem pode ter um desempenho melhor, pois a transferência é concluída com uma única solicitação e evita a sobrecarga de várias solicitações.

Se você não tiver certeza de qual valor é melhor para sua situação, uma opção segura é definir InitialTransferSize como o mesmo valor usado para MaximumTransferSize.

Observação

Ao usar um objeto BlobClient, o carregamento de um blob menor que o InitialTransferSize será executado usando Put Blob, em vez de Put Block.

MaximumConcurrency

MaximumConcurrency é o número máximo de trabalhos que podem ser usados em uma transferência paralela. No momento, somente operações assíncronas podem paralelizar transferências. As operações síncronas ignoram esse valor e funcionam em sequência.

A eficácia desse valor está sujeita aos limites do pool de conexões no .NET, o que pode restringir o desempenho por padrão em determinados cenários. Para saber mais sobre os limites do pool de conexões no .NET, confira Limites do pool de conexões do .NET Framework e o novo SDK do Azure para .NET.

MaximumTransferSize

MaximumTransferSize é a duração máxima de uma transferência em bytes. Conforme mencionado anteriormente, esse valor não limita InitialTransferSize, que pode ser maior que MaximumTransferSize.

Para manter os dados em movimento com eficiência, as bibliotecas de cliente nem sempre podem alcançar o valor MaximumTransferSize de cada transferência. Dependendo da operação, o valor máximo com suporte para o tamanho da transferência pode variar. Por exemplo, os blobs de blocos que chamam a operação Put Block com uma versão de serviço de 2019-12-12 ou posterior têm um tamanho máximo de bloco de 4000 MiB. Para obter mais informações sobre limites de tamanho de transferência para o Armazenamento de Blobs, confira o gráfico em Dimensionar destinos para armazenamento de Blobs.

Exemplo de código

A biblioteca de clientes inclui sobrecargas para os métodos Upload e UploadAsync, que aceitam uma instância de storageTransferOptions como parte de um parâmetro BlobUploadOptions. Sobrecargas semelhantes também existem para os métodos DownloadTo e DownloadToAsync, usando o parâmetro BlobDownloadToOptions.

O exemplo de código a seguir mostra como definir valores para uma instância StorageTransferOptions e passar essas opções de configuração como parâmetro para UploadAsync. Os valores fornecidos neste exemplo não se destinam a ser uma recomendação. Para ajustar adequadamente esses valores, você precisa considerar as necessidades específicas do seu aplicativo.

// Specify the StorageTransferOptions
BlobUploadOptions options = new BlobUploadOptions
{
    TransferOptions = new StorageTransferOptions
    {
        // Set the maximum number of parallel transfer workers
        MaximumConcurrency = 2,

        // Set the initial transfer length to 8 MiB
        InitialTransferSize = 8 * 1024 * 1024,

        // Set the maximum length of a transfer to 4 MiB
        MaximumTransferSize = 4 * 1024 * 1024
    }
};

// Upload data from a stream
await blobClient.UploadAsync(stream, options);

Neste exemplo, definimos o número de trabalhos de transferência paralela como 2, usando a propriedade MaximumConcurrency. Essa configuração abre até duas conexões simultaneamente, permitindo que o upload ocorra em paralelo. A solicitação inicial do intervalo HTTP tenta carregar até 8 MiB de dados, conforme definido pela propriedade InitialTransferSize. Observe que InitialTransferSize só se aplica a uploads ao usar um fluxo pesquisável. Se o tamanho do blob for menor que 8 MiB, apenas uma única solicitação será necessária para concluir a operação. Se o tamanho do blob for maior que 8 MiB, todas as solicitações de transferência subsequentes terão um tamanho máximo de 4 MiB, que definimos com a propriedade MaximumTransferSize.

Considerações de desempenho para uploads

Durante um upload, as bibliotecas do cliente de armazenamento dividem um determinado fluxo de upload em vários subuploads com base nos valores definidos na instância StorageTransferOptions. Cada subupload tem sua própria chamada dedicada para a operação REST. Para um objeto BlobClient ou objeto BlockBlobClient, essa operação é Put Block. Para um objeto DataLakeFileClient, essa operação é Acrescentar Dados. A biblioteca de clientes de armazenamento gerencia essas operações REST paralelamente (dependendo das opções de transferência) para concluir o upload completo.

Dependendo se o fluxo de upload é buscável ou não, a biblioteca do cliente lida com o buffer e InitialTransferSize de maneira diferente, conforme descrito nas seções a seguir. Um fluxo pesquisável é um fluxo que dá suporte à consulta e à modificação da posição atual em um fluxo. Para saber mais sobre fluxos no .NET, confira a referência da classe de Transmissão.

Observação

Os blobs de blocos têm uma contagem máxima de blocos de 50.000 blocos. Então, o tamanho máximo do blob de blocos é 50.000 vezes MaximumTransferSize.

Buffer durante os uploads

A camada REST de Armazenamento não dá suporte à coleta de uma operação de upload REST de onde você parou. As transferências individuais são concluídas ou perdidas. Para garantir a resiliência para uploads de fluxos não pesquisáveis, as bibliotecas de clientes de armazenamento colocam os dados em buffer para cada chamada REST individual, antes de iniciar o upload. Além das limitações de velocidade de rede, esse comportamento de buffer é motivo para considerar um valor menor para MaximumTransferSize, mesmo ao carregar em sequência. Diminuir o valor de MaximumTransferSize diminui a quantidade máxima de dados armazenados em buffer em cada solicitação e em cada nova tentativa de uma solicitação com falha. Se você estiver enfrentando tempos limite frequentes durante as transferências de dados de um determinado tamanho, a redução do valor de MaximumTransferSize reduz o tempo de armazenamento em buffer e pode resultar em um melhor desempenho.

Outro cenário em que o buffer ocorre é quando você está carregando dados com chamadas REST paralelas, para maximizar a taxa de transferência de rede. As bibliotecas do cliente precisam de fontes das quais possam ser lidas em paralelo e, como os fluxos são sequenciais, as bibliotecas do cliente de armazenamento armazenam em buffer os dados de cada chamada REST individual antes de iniciar o upload. Esse comportamento de buffer ocorre mesmo que o fluxo fornecido seja pesquisável.

Para evitar o buffer durante uma chamada de upload assíncrona, você deve fornecer um fluxo pesquisável e definir MaximumConcurrency como 1. Embora essa estratégia deva funcionar na maioria das situações, ainda é possível que o buffer ocorra se o código estiver usando outros recursos da biblioteca de clientes que exigem buffer.

InitialTransferSize no upload

Quando um fluxo buscável é fornecido para upload, o comprimento do fluxo é verificado em relação ao valor de InitialTransferSize. Se o comprimento do fluxo for inferior a esse valor, todo o fluxo será carregado como uma única chamada REST, independentemente de outros valores StorageTransferOptions. Caso contrário, o upload é feito em várias partes, conforme descrito anteriormente. InitialTransferSize não tem efeito em um fluxo não buscável e será ignorado.

Considerações de desempenho para downloads

Durante um download, as bibliotecas do cliente de armazenamento dividem uma determinada solicitação de download em vários subdownloads com base nos valores definidos na instância StorageTransferOptions. Cada subdownload tem sua própria chamada dedicada para a operação REST. Dependendo das opções de transferência, as bibliotecas de clientes gerenciarão essas operações REST paralelamente para concluir o download completo.

Buffer durante os downloads

Receber várias respostas HTTP simultaneamente com o conteúdo do corpo tem implicações no uso de memória. No entanto, as bibliotecas de clientes de armazenamento não adicionam explicitamente uma etapa de buffer para o conteúdo baixado. As respostas de entrada são processadas em ordem. As bibliotecas de clientes configuram um buffer de 16 quilobytes para copiar os fluxos de um fluxo de resposta HTTP para um fluxo de destino ou caminho de arquivo fornecido pelo chamador.

InitialTransferSize no download

Durante um download, as bibliotecas do cliente de armazenamento fazem uma solicitação de intervalo de download usando InitialTransferSize antes de fazer qualquer outra coisa. Durante essa solicitação inicial de download, as bibliotecas do cliente sabem o tamanho total do recurso. Se a solicitação inicial tiver baixado todo o conteúdo com êxito, a operação será concluída. Caso contrário, as bibliotecas do cliente continuarão fazendo solicitações de intervalo de até MaximumTransferSize até que o download completo seja concluído.

Próximas etapas