为什么上传到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 list = new List(); 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加速进程?

为什么只有2.5 MB /秒,但不像官方网站那样60 MB /秒或http://blogs.microsoft.co.il/applisec/2012/01/05/windows-azure-benchmarks-part-2-blob -write通量/

和你一样,我也有很多关于页面blob的性能问题 – 即使它们不是那么严重。 看起来你已经完成了你的作业,我可以看到你正在做这本书的所有内容。

要检查的一些事项:

  • 确保您的VM没有交换(您可以登录远程桌面)。 例如,如果您问我,超小型768MB内存VM实际上太小了,无法满足任何实际应用。
  • 设置自己的连接限制,尤其是在运行小型VM的情况下。 ServicePointManager.DefaultConnectionLimit
  • 较大的页面将为您提供更多性能。
  • 写入多个线程(例如,使用Task s / async / await ,特别是如果您有很多事情要做)。

还有件事儿:

  • 不要将仿真器用于这些类型的事情。 模拟器不是真实世界Azure的良好代表,绝对是wrt。 基准。

您访问时间缓慢的主要原因是因为您正在同步执行所有操作。 microsoft的基准测试可以访问多个线程中的blob,这将提供更多的吞吐量。

现在,Azure也知道性能是一个问题,这就是为什么他们试图通过本地缓存支持存储来缓解这个问题。 这里基本上发生的是他们将数据写入本地(f.ex.在文件中),然后将任务切成碎片,然后使用多个线程将所有内容写入blob存储。 数据存储运动库就是这样一个库。 但是,在使用它们时,您应该始终牢记这些具有不同的持久性约束(就像在本地PC上启用“写入缓存”)并且可能会破坏您设置分布式系统的方式(如果您读取和写入相同的内容)来自多个VM的存储)。

为什么…

你问过’为什么’。 为了理解blob存储缓慢的原因,您需要了解它的工作原理。 首先,我想指出Microsoft Azure的这个演示文稿解释了Azure存储的实际工作原理。

您应该意识到的第一件事是Azure存储由一组分布式(旋转)磁盘支持。 由于持久性和一致性限制,它们还确保将数据写入稳定存储的“多数投票”。 对于性能,系统的几个级别将具有高速缓存,其主要是读取高速缓存(同样,由于持久性约束)。

现在,Azure团队不会发布所有内容。 幸运的是,5年前,我以前的公司在较小规模上创建了一个类似的系统。 我们遇到类似Azure的性能问题,系统与我上面链接的演示文稿非常相似。 因此,我想我可以解释和推测瓶颈在哪里。 为清楚起见,我会将章节标记为我认为合适的推测。

如果您将页面写入Blob存储,您实际上设置了一系列TCP / IP连接,将页面存储在多个位置,并且当收到多数投票时,您向客户端返回“ok”。 现在,这个系统实际上存在一些瓶颈:

  1. 您必须在整个基础架构中设置一系列TCP / IP连接。 设置这些将花费时间。
  2. 存储的端点必须执行磁盘搜索到正确的位置,并执行操作。
  3. 地理复制当然比本地复制花费更多时间。
  4. [推测]我们还发现在“缓冲”阶段花了很多时间。

这里的数字(1),(2)和(3)是众所周知的。 这里的数字(4)实际上是(1)和(2)的结果。 请注意,您不能仅向旋转磁盘抛出无限数量的请求; 嗯……实际上你可以,但随后系统将停止运转。 因此,为了解决这个问题,来自不同客户端的磁盘搜索通常以这样一种方式安排,即只要您知道自己也可以编写所有内容(以最大限度地减少昂贵的搜索)。 但是,这里存在一个问题:如果你想提高吞吐量,你需要在拥有所有数据之前开始寻找 – 如果你没有足够快地获得数据,其他请求必须等待更长时间。 这也是一个两难的问题:你可以为此进行优化(这有时会损害每个客户端的吞吐量并阻止其他所有人,尤其是混合工作负载)或缓冲所有内容然后一次查找和写入所有内容(这更容易,但添加了一些每个人的延迟)。 由于Azure提供了大量客户端,我怀疑他们选择了最后一种方法 – 这为整个写周期增加了更多延迟。

尽管如此,大部分时间可能会花费在(1)和(2)上。 实际的数据突发和数据写入非常快。 为了给你一个粗略的估计: 这里有一些常用的时间 。

所以,这给我们留下了一个问题: 为什么在多个线程中写东西的速度要快得多?

其原因实际上非常简单:如果我们在多个线程中编写内容,那么我们很有可能将实际数据存储在不同的服务器上。 这意味着我们可以将我们的瓶颈从“搜索+网络设置延迟”转移到“吞吐量”。 只要我们的客户端VM可以处理它,基础设施很可能也可以处理它。

如果您不介意处理文件而不是流(或者这可能有流支持而且我不知道它),请查看Azure存储数据移动库。 这是迄今为止我见过的最好的。

它是相对较新的(在编写本文时),但非常好地支持以块的forms移动大文件并最大化吞吐量(我将其用于夜间复制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((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/

一个简单快速的检查方法:确保您的blob存储位于运行VM或Application的同一Azure区域中。 我们遇到的一个问题是我们的存储帐户位于我们应用程序的另一个区域。 这导致我们在处理过程中出现严重延迟。 在我们意识到我们正在跨地区阅读和写作之前,我们正在摸不着头脑。 菜鸟队的错误就是我们的错!