web-dev-qa-db-ja.com

Azure BLOBへのアップロードが非常に遅いのはなぜですか?

ページクラウドBLOBへの書き込み操作を直接実行するために使用されるカスタムストリームがあります。

_public sealed class WindowsAzureCloudPageBlobStream : Stream
{
    // 4 MB is the top most limit for page blob write operations
    public const int MaxPageWriteCapacity = 4 * 1024 * 1024;

    // Every operation on a page blob has to manipulate a value which is rounded up to 512 bytes
    private const int PageBlobPageAdjustmentSize = 512;

    private CloudPageBlob _pageBlob;

    public override void Write(byte[] buffer, int offset, int count)
    {
        var additionalOffset = 0;
        var bytesToWriteTotal = count;

        List<Task> list = new List<Task>();
        while (bytesToWriteTotal > 0)
        {
            var bytesToWriteTotalAdjusted = RoundUpToPageBlobSize(bytesToWriteTotal);

            // Azure does not allow us to write as many bytes as we want
            // Max allowed size per write is 4MB
            var bytesToWriteNow = Math.Min((int)bytesToWriteTotalAdjusted, MaxPageWriteCapacity);
            var adjustmentBuffer = new byte[bytesToWriteNow];
            ...
            var memoryStream = new MemoryStream(adjustmentBuffer, 0, bytesToWriteNow, false, false);
            var task = _pageBlob.WritePagesAsync(memoryStream, Position, null);
            list.Add(task);
        }

        Task.WaitAll(list.ToArray());
    }

    private static long RoundUpToPageBlobSize(long size) 
    { 
        return (size + PageBlobPageAdjustmentSize - 1) & ~(PageBlobPageAdjustmentSize - 1); 
    }
_

Write()のパフォーマンスが低いです。例えば:

_Stopwatch s = new Stopwatch();
s.Start();
using (var memoryStream = new MemoryStream(adjustmentBuffer, 0, bytesToWriteNow, false, false))
{
      _pageBlob.WritePages(memoryStream, Position);
}

s.Stop();
Console.WriteLine(s.Elapsed); => 00:00:01.52 == Average speed 2.4 MB/s
_

どうすればアルゴリズムを改善できますか? _Parallel.ForEach_を使用してプロセスを高速化する方法は?

公式サイト または http://blogs.Microsoft.co.il/applisec/2012/01/05/)のように、なぜ2.5 MB /秒だけで、60MB /秒ではないのですか。 windows-Azure-benchmarks-part-2-blob-write-throughput /

11
Anatoly

あなたと同じように、ページBLOBについても、それほど深刻ではありませんが、パフォーマンスの問題がたくさんありました。あなたは宿題をしたようです、そして私はあなたが本によってすべてをしているのを見ることができます。

チェックするいくつかの事柄:

  • VMがスワップされていないことを確認してください(リモートデスクトップでチェックインできます)。たとえば、非常に小さい768MBのメモリVMは、実際に使用するには小さすぎます。
  • 特に小さなVMを実行している場合は、独自の接続制限を設定します。 ServicePointManager.DefaultConnectionLimit
  • ページが大きいほど、パフォーマンスが向上します。
  • 複数のスレッドで書き込みます(たとえば、特にやることがたくさんある場合は、Tasks/async/awaitを使用します)。

ああ、もう1つ:

  • このようなことにはエミュレータを使用しないでください。エミュレーターは、実際のAzureを適切に表現したものではなく、間違いなくwrtです。ベンチマーク。

アクセス時間が遅い主な理由は、すべてを同期的に実行しているためです。 Microsoftのベンチマークは、複数のスレッドのBLOBにアクセスするため、スループットが向上します。

現在、Azureはパフォーマンスが問題であることも認識しています。そのため、ローカルキャッシュを使用してストレージをバックアップすることで問題を軽減しようとしました。ここで基本的に行われるのは、データをローカル(ファイルなど)に書き込み、タスクを細かく分割してから、複数のスレッドを使用してすべてをBLOBストレージに書き込むことです。データストレージ移動ライブラリは、そのようなライブラリの1つです。ただし、これらを使用する場合は、耐久性の制約が異なり(ローカルPCで「書き込みキャッシュ」を有効にするようなものです)、分散システムのセットアップ方法が損なわれる可能性があることに常に注意する必要があります(同じものを読み書きする場合)複数のVMからのストレージ)。

なぜ...

あなたは「なぜ」を求めました。 BLOBストレージが遅い理由を理解するには、それがどのように機能するかを理解する必要があります。まず、Azureストレージが実際にどのように機能するかを説明するMicrosoft Azureからの このプレゼンテーション があることを指摘したいと思います。

最初に認識しておくべきことは、Azureストレージは分散された(回転する)ディスクのセットによって支えられているということです。耐久性と一貫性の制約があるため、データが安定したストレージに書き込まれるという「多数決」も確実に行われます。パフォーマンスのために、システムのいくつかのレベルにキャッシュがあり、ほとんどが読み取りキャッシュになります(これも耐久性の制約のためです)。

現在、Azureチームはすべてを公開しているわけではありません。私にとって幸いなことに、5年前、私の前の会社は小規模で同様のシステムを作成しました。 Azureと同様のパフォーマンスの問題があり、システムは上記でリンクしたプレゼンテーションと非常に似ていました。そのため、ボトルネックがどこにあるかについて少し説明し、推測できると思います。わかりやすくするために、これが適切だと思うセクションを推測としてマークします。

BLOBストレージにページを書き込む場合、実際には一連のTCP/IP接続をセットアップし、ページを複数の場所に保存し、多数決を受け取ったら、クライアントに「OK」を返します。現在、このシステムには実際にいくつかのボトルネックがあります。

  1. インフラストラクチャ全体で一連のTCP/IP接続を設定する必要があります。これらの設定には時間がかかります。
  2. ストレージのエンドポイントは、正しい場所へのディスクシークを実行し、操作を実行する必要があります。
  3. もちろん、ジオレプリケーションはローカルレプリケーションよりも時間がかかります。
  4. [推測]また、「バッファリング」フェーズで多くの時間が費やされたことがわかりました。

ここでの番号(1)、(2)、(3)は非常によく知られています。ここでの数(4)は、実際には(1)と(2)の結果です。回転しているディスクに無限の数のリクエストをスローすることはできないことに注意してください。ええと...実際にはできますが、そうするとシステムはひどく停止します。したがって、これを解決するために、通常、さまざまなクライアントからのディスクシークは、すべてを書き込むことができることがわかっている場合にのみシークするようにスケジュールされます(コストのかかるシークを最小限に抑えるため)。ただし、ここには問題があります。スループットをプッシュする場合は、すべてのデータを取得する前にシークを開始する必要があります。データを十分に速く取得できない場合は、他のリクエストをより長く待機する必要があります。ここにもジレンマがあります。これを最適化するか(これにより、クライアントごとのスループットが低下し、特にワークロードが混在している場合は、他のすべてのユーザーが停止する可能性があります)、すべてをバッファリングしてから、すべてを一度にシークして書き込むことができます(これは簡単ですが、いくつか追加されます)すべての人の待ち時間)。 Azureが提供するクライアントの数が非常に多いため、最後のアプローチを選択したのではないかと思います。これにより、書き込みサイクル全体のレイテンシーが増加します。

それにもかかわらず、ほとんどの時間はおそらく(1)と(2)によって費やされます。その場合、実際のデータバーストとデータ書き込みは非常に高速です。大まかな見積もりを出すために: ここにいくつかの一般的に使用されるタイミングがあります

だから、それは私たちに1つの質問を残します:なぜ複数のスレッドでものを書くのがそれほど速くなるのですか?

その理由は実際には非常に単純です。複数のスレッドでデータを書き込む場合、実際のデータを異なるサーバーに保存する可能性が高くなります。これは、ボトルネックを「シーク+ネットワークセットアップの待ち時間」から「スループット」にシフトできることを意味します。そして、クライアントVMがそれを処理できる限り、インフラストラクチャもそれを処理できる可能性が非常に高くなります。

10
atlaste

ストリームの代わりにファイルを処理してもかまわない場合(または、ストリームがサポートされていて、それについてはわかりません)、Azure Storage Data MovementLibraryをご覧ください。それは私が今まで見た中で最高です。

これは(執筆時点では)比較的新しいものですが、大きなファイルをチャンクで移動し、スループットを最大化するための非常に優れたサポートを備えています(SQLバックアップの夜間コピーに使用します。多くはサイズが1GBを超えます)。

https://Azure.Microsoft.com/en-us/blog/announcing-Azure-storage-data-movement-library-0-2-0/

使い方はとても簡単です。次に例を示します。

using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.DataMovement;


namespace BlobUploader
{
    public class Uploader
    {

        public string ConnectionString { get; set; }
        public string ContainerName { get; set; }
        public string BlobName { get; set; }

        public void UploadFile(string filePath) {

            CloudStorageAccount account = CloudStorageAccount.Parse(ConnectionString);
            CloudBlobClient blobClient = account.CreateCloudBlobClient();
            CloudBlobContainer blobContainer = blobClient.GetContainerReference(ContainerName);
            blobContainer.CreateIfNotExists();
            CloudBlockBlob destinationBlob = blobContainer.GetBlockBlobReference(BlobName);

            TransferManager.Configurations.ParallelOperations = 64;

            TransferContext context = new TransferContext();
            context.ProgressHandler = new Progress<TransferProgress>((progress) => {
                Console.WriteLine("Bytes uploaded: {0}", progress.BytesTransferred);
            });

            var task = TransferManager.UploadAsync(filePath, destinationBlob, null, context, CancellationToken.None);
            task.Wait();   
        }


    }
}

次のプレビューブログ投稿は、これがどのように発生し、一般的にどのようにアプローチするかについての情報を提供します。

https://Azure.Microsoft.com/en-us/blog/introducing-Azure-storage-data-movement-library-preview-2/

4
jleach

確認する簡単で迅速なことの1つ:blobストレージがVMまたはアプリケーションが実行されているのと同じAzureリージョンにあることを確認します。発生した問題の1つは、ストレージアカウントが別のリージョンにあることでした。これにより、処理中に大幅な遅延が発生しました。リージョン間で読み取りと書き込みを行っていることに気付くまで、頭を悩ませていました。ルーキーのミスです。

1
cnaegle