Ottimizzazione delle prestazioni per caricamenti e download con .NET
Quando un'applicazione trasferisce i dati usando la libreria client di Archiviazione di Azure per .NET, esistono diversi fattori che possono influire sulla velocità, sull'utilizzo della memoria e persino sull'esito positivo o negativo della richiesta. Per ottimizzare le prestazioni e l'affidabilità per i trasferimenti di dati, è importante essere proattivi nella configurazione delle opzioni di trasferimento della libreria client in base all'ambiente in cui viene eseguita l'app.
Questo articolo illustra diverse considerazioni per l'ottimizzazione delle opzioni di trasferimento dei dati, che si applicano a qualsiasi API che accetta StorageTransferOptions
come parametro. Se ottimizzata correttamente, la libreria client può distribuire in modo efficiente i dati tra più richieste, con conseguente miglioramento della velocità operativa, dell'utilizzo della memoria e della stabilità di rete.
Ottimizzazione delle prestazioni con StorageTransferOptions
L'ottimizzazione corretta dei valori in StorageTransferOptions è fondamentale per ottenere prestazioni affidabili per le operazioni di trasferimento dei dati. I trasferimenti di archiviazione vengono partizionati in diversi sottotrasferimenti in base ai valori di proprietà definiti in un'istanza di questo struct. Le dimensioni massime di trasferimento supportate variano in base all'operazione e alla versione del servizio, quindi assicurarsi di controllare la documentazione per determinare i limiti. Per altre informazioni sui limiti delle dimensioni di trasferimento per l'archiviazione BLOB, vedere Dimensionare le destinazioni per l'archiviazione BLOB.
Le proprietà seguenti di StorageTransferOptions
possono essere ottimizzate in base alle esigenze dell'app:
- InitialTransferSize: dimensioni della prima richiesta in byte
- MaximumConcurrency: numero massimo di sottotrasferimenti utilizzabili in parallelo
- MaximumTransferSize: lunghezza massima di un trasferimento in byte
Nota
Mentre lo struct StorageTransferOptions
contiene valori nullable, le librerie client useranno le impostazioni predefinite per ogni singolo valore, se non specificato. Queste impostazioni predefinite sono in genere efficienti in un ambiente data center, ma non sono adatte per gli ambienti consumer domestici. Le opzioni StorageTransferOptions
non ottimizzate possono comportare operazioni eccessivamente lunghe e persino timeout delle richieste. È consigliabile essere proattivi nel testare i valori in StorageTransferOptions
e ottimizzarli in base alle esigenze dell'applicazione e dell'ambiente.
InitialTransferSize
InitialTransferSize rappresenta le dimensioni della prima richiesta di intervallo in byte. Una richiesta di intervallo HTTP è una richiesta parziale, con le dimensioni definite da InitialTransferSize
in questo caso. I BLOB inferiori a queste dimensioni vengono trasferiti in una singola richiesta. I BLOB maggiori di questa dimensione continuano a essere trasferiti in blocchi di dimensioni MaximumTransferSize
.
È importante notare che il valore specificato per MaximumTransferSize
non limita il valore definito per InitialTransferSize
. InitialTransferSize
definisce una limitazione delle dimensioni separata per una richiesta iniziale in modo da eseguire l'intera operazione contemporaneamente, senza sottotrasferimenti. Spesso si vuole che InitialTransferSize
sia almeno grande quanto il valore definito per MaximumTransferSize
, se non più grande. A seconda delle dimensioni del trasferimento dei dati, questo approccio può essere più efficiente, poiché il trasferimento viene completato con una singola richiesta ed evita il sovraccarico di più richieste.
Se non si è certi del valore migliore per la situazione, un'opzione sicura consiste nell'impostare InitialTransferSize
sullo stesso valore usato per MaximumTransferSize
.
Nota
Quando si usa un oggetto BlobClient
, il caricamento di un BLOB inferiore a InitialTransferSize
verrà eseguito usando Put Blob, anziché Put Block.
MaximumConcurrency
MaximumConcurrency è il numero massimo di ruoli di lavoro che possono essere usati in un trasferimento parallelo. Attualmente, solo le operazioni asincrone possono parallelizzare i trasferimenti. Le operazioni sincrone ignorano questo valore e funzionano in sequenza.
L'efficacia di questo valore è soggetta ai limiti del pool di connessioni in .NET, che può limitare le prestazioni per impostazione predefinita in determinati scenari. Per altre informazioni sui limiti del pool di connessioni in .NET, vedere Limiti del pool di connessioni .NET Framework e il nuovo Azure SDK per .NET.
MaximumTransferSize
MaximumTransferSize è lunghezza massima di un trasferimento in byte. Come accennato in precedenza, questo valore non limita InitialTransferSize
, che può essere maggiore di MaximumTransferSize
.
Per mantenere efficiente lo spostamento dei dati, le librerie client potrebbero non raggiungere sempre il valore MaximumTransferSize
per ogni trasferimento. A seconda dell'operazione, il valore massimo supportato per le dimensioni del trasferimento può variare. Ad esempio, i BLOB in blocchi che chiamano l'operazione Put Block con una versione del servizio 2019-12-12 o successiva hanno una dimensione massima del blocco pari a 4000 MiB. Per altre informazioni sui limiti delle dimensioni di trasferimento per l'archiviazione BLOB, vedere il grafico in Dimensionare le destinazioni per l'archiviazione BLOB.
Esempio di codice
La libreria client include overload per i metodi Upload
e UploadAsync
, che accettano un'istanza di StorageTransferOptions come parte di un parametro BlobUploadOptions. Esistono anche overload simili per i metodi DownloadTo
e DownloadToAsync
, usando un parametro BlobDownloadToOptions.
Nell'esempio di codice seguente viene illustrato come definire i valori per un'istanza di StorageTransferOptions
e passare queste opzioni di configurazione come parametro a UploadAsync
. I valori forniti in questo esempio non sono necessariamente quelli consigliati. Per ottimizzare correttamente questi valori, è necessario considerare le esigenze specifiche dell'app.
// 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);
In questo esempio il numero di ruoli di lavoro di trasferimento parallelo viene impostato su 2, usando la proprietà MaximumConcurrency
. Questa configurazione si apre contemporaneamente a due connessioni, consentendo il caricamento in parallelo. La richiesta di intervallo HTTP iniziale tenta di caricare fino a 8 MiB di dati, come definito dalla proprietà InitialTransferSize
. Si noti che InitialTransferSize
si applica solo per i caricamenti quando si usa un flusso ricercabile. Se le dimensioni del BLOB sono inferiori a 8 MiB, è necessaria solo una singola richiesta per completare l'operazione. Se le dimensioni del BLOB sono maggiori di 8 MiB, tutte le richieste di trasferimento successive hanno una dimensione massima di 4 MiB, impostata con la proprietà MaximumTransferSize
.
Considerazioni sulle prestazioni per i caricamenti
Durante un caricamento, le librerie client di archiviazione suddividono un determinato flusso di caricamento in più sottocaricamenti in base ai valori definiti nell'istanza di StorageTransferOptions
. Ogni sottocaricamento ha una propria chiamata dedicata all'operazione REST. Per un oggetto BlobClient
o BlockBlobClient
, questa operazione è Put Block. Per un oggetto DataLakeFileClient
, questa operazione è Append Data. La libreria client di archiviazione gestisce queste operazioni REST in parallelo (a seconda delle opzioni di trasferimento) per completare il caricamento.
A seconda che il flusso di caricamento sia ricercabile o non ricercabile, la libreria client gestisce il buffering e InitialTransferSize
in modo diverso, come descritto nelle sezioni seguenti. Un flusso ricercabile è un flusso che supporta l'esecuzione di query e la modifica della posizione corrente all'interno di un flusso stesso. Per altre informazioni sui flussi in .NET, vedere le informazioni di riferimento sulla classe Flusso.
Nota
I BLOB in blocchi hanno un numero massimo di 50.000 blocchi. Le dimensioni massime del BLOB in blocchi, quindi, sono pari a 50.000 volte MaximumTransferSize
.
Buffering durante i caricamenti
Il livello REST di archiviazione non supporta la ripresa di un'operazione di caricamento REST che è stata interrotta. I singoli trasferimenti vengono completati o persi. Per garantire la resilienza per i caricamenti di flussi non ricercabili, le librerie client di archiviazione eseguono il buffer dei dati per ogni singola chiamata REST prima di avviare il caricamento. Oltre alle limitazioni della velocità di rete, questo comportamento di buffering è un motivo per considerare un valore più piccolo per MaximumTransferSize
, anche quando si effettuano caricamenti in sequenza. La riduzione del valore di MaximumTransferSize
riduce la quantità massima di dati memorizzati nel buffer per ogni richiesta e per ogni retry di una richiesta non riuscita. Se si verificano timeout frequenti durante i trasferimenti di dati di una determinata dimensione, riducendo il valore di MaximumTransferSize
si riduce il tempo di memorizzazione nel buffer e si possono ottenere prestazioni migliori.
Un altro scenario in cui si verifica il buffering è quando si caricano dati con chiamate REST parallele per ottimizzare la velocità effettiva di rete. Le librerie client necessitano di origini leggibili in parallelo e, poiché i flussi sono sequenziali, le librerie client di archiviazione memorizzano nel buffer i dati per ogni singola chiamata REST prima di avviare il caricamento. Questo comportamento di buffering si verifica anche se il flusso fornito è ricercabile.
Per evitare il buffering durante una chiamata di caricamento asincrona, è necessario fornire un flusso ricercabile e impostare MaximumConcurrency
su 1. Anche se questa strategia dovrebbe essere ideale per la maggior parte delle situazioni, è comunque possibile che si verifichi buffering se il codice usa altre funzionalità della libreria client che richiedono il buffering.
InitialTransferSize al caricamento
Quando viene fornito un flusso ricercabile per il caricamento, la lunghezza del flusso viene verificata rispetto al valore di InitialTransferSize
. Se la lunghezza del flusso è minore di questo valore, l'intero flusso viene caricato come singola chiamata REST, indipendentemente da altri valori StorageTransferOptions
. In caso contrario, il caricamento viene eseguito in più parti, come descritto in precedenza. InitialTransferSize
non ha alcun effetto su un flusso non ricercabile e viene ignorato.
Considerazioni sulle prestazioni per i download
Durante un download, le librerie client di archiviazione suddividono una determinata richiesta di download in più download secondari in base ai valori definiti nell'istanza di StorageTransferOptions
. Ogni sottodownload ha una propria chiamata dedicata all'operazione REST. A seconda delle opzioni di trasferimento, le librerie client gestiscono queste operazioni REST in parallelo per completare il download.
Buffering durante i download
La ricezione simultanea di più risposte HTTP con il contenuto del corpo ha implicazioni per l'utilizzo della memoria. Tuttavia, le librerie client di archiviazione non aggiungono in modo esplicito un passaggio del buffer per il contenuto scaricato. Le risposte in ingresso vengono elaborate in ordine. Le librerie client configurano un buffer da 16 kilobyte per la copia di flussi da un flusso di risposta HTTP a un flusso di destinazione o a un percorso di file fornito dal chiamante.
InitialTransferSize al download
Durante un download, le librerie client di archiviazione effettuano una richiesta di intervallo di download usando InitialTransferSize
prima di eseguire qualsiasi altra operazione. Durante questa richiesta di download iniziale, le librerie client conoscono le dimensioni totali della risorsa. Se la richiesta iniziale ha scaricato tutto il contenuto, l'operazione viene completata. In caso contrario, le librerie client continuano a effettuare richieste di intervallo fino a MaximumTransferSize
, fino al completamento del download.
Passaggi successivi
- Questo articolo fa parte della Guida per sviluppatori di Archiviazione BLOB per .NET. Vedere l’elenco completo degli articoli della guida per sviluppatori in Creare la propria app.
- Per altre informazioni sui fattori che possono influenzare le prestazioni per le operazioni di Archiviazione di Azure, vedere Latenza nell'archiviazione BLOB.
- Per un elenco delle considerazioni di progettazione per ottimizzare le prestazioni per le app che usano l'archiviazione BLOB, vedere Elenco di controllo per prestazioni e scalabilità dell'archiviazione BLOB.