使用 .NET 对上传和下载进行性能优化

当应用程序使用适用于 .NET 的 Azure 存储客户端库传输数据时,有几个因素可能会影响请求的速度、内存使用情况,甚至影响请求是否成功。 若要最大程度提高数据传输的性能和可靠性,必须主动根据应用运行的环境来配置客户端库传输选项。

本文介绍了优化数据传输选项的几个注意事项,相关指南适用于接受 StorageTransferOptions 作为参数的任何 API。 正确优化后,客户端库可以有效地跨多个请求分发数据,从而提高操作速度、内存使用率和网络稳定性。

使用 StorageTransferOptions 进行性能优化

正确优化 StorageTransferOptions 中的值是确保数据传输操作性能可靠的关键。 根据此结构的实例中定义的属性值,将存储传输划分为多个子传输。 支持的最大传输大小因操作和服务版本而异,因此请务必查看文档以确定限制。 若要详细了解 Blob 存储的传输大小限制,请参阅 Blob 存储的缩放目标

可根据应用需求优化 StorageTransferOptions 的以下属性:

注意

虽然 StorageTransferOptions 结构包含可为 null 的值,但如果未提供,客户端库将为每个单独的值使用默认值。 这些默认值通常在数据中心环境中表现良好,但不太可能适合家庭使用者环境。 StorageTransferOptions 优化不当可能会导致操作时间过长,甚至请求超时。 最好主动测试 StorageTransferOptions 中的值,并根据应用程序和环境的需求优化它们。

InitialTransferSize

InitialTransferSize 是第一个范围请求的大小(以字节为单位)。 HTTP 范围请求是部分请求,在本例中大小由 InitialTransferSize 定义。 小于此大小的 Blob 在单个请求中传输。 大于此大小的 Blob 继续以 MaximumTransferSize 大小的区块传输。

请务必注意,为 MaximumTransferSize 指定的值不会限制为 InitialTransferSize 定义的值。 InitialTransferSize 为一次执行整个操作的初始请求定义了单独的大小限制,没有子传输。 通常情况下,你希望 InitialTransferSize 至少与你为 MaximumTransferSize 定义的值一样大(即便不是更大)。 根据数据传输的大小,这种方法可以更高效,因为传输是通过单个请求完成的,避免了多个请求的开销。

如果不确定什么值最适合你的情况,一个安全的选择是将 InitialTransferSize 设置为用于 MaximumTransferSize 的值。

注意

使用 BlobClient 对象时,上传小于 InitialTransferSize 的 blob 将使用放置 Blob 而不是放置块执行。

MaximumConcurrency

MaximumConcurrency 是可以在并行传输中使用的最大辅助角色数。 目前,只有异步操作可以并行传输。 同步操作会忽略此值并按顺序工作。

此值的有效性受 .NET 中的连接池限制的约束,这可能会在某些情况下默认限制性能。 若要详细了解 .NET 中的连接池限制,请参阅 .NET Framework 连接池限制和适用于 .NET 的新 Azure SDK

MaximumTransferSize

MaximumTransferSize 是传输的最大长度(以字节为单位)。 如前所述,此值不限制 InitialTransferSize,后者可以大于 MaximumTransferSize

为了保持数据高效移动,客户端库可能不会每次传输都达到 MaximumTransferSize 值。 根据操作的不同,传输大小的最大支持值可能会有所不同。 例如,使用 2019-12-12 或更高版本的服务调用放置块操作的块 blob 的最大块大小为 4000 MiB。 若要详细了解 Blob 存储的传输大小限制,请参阅 Blob 存储的缩放目标中的图表。

代码示例

客户端库包括 UploadUploadAsync 方法的重载,它们接受 StorageTransferOptions 实例作为 BlobUploadOptions 参数的一部分。 DownloadToDownloadToAsync 方法也存在类似的重载(使用 BlobDownloadToOptions 参数)。

以下代码示例显示如何定义 StorageTransferOptions 实例的值并将这些配置选项作为参数传递给 UploadAsync。 此示例中提供的值不作为建议。 若要正确优化这些值,需要考虑应用的特定需求。

// 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);

在此示例中,我们使用 MaximumConcurrency 属性将并行传输辅助角色的数量设置为 2。 此配置最多可同时打开 2 个连接,从而允许并行上传。 初始 HTTP 范围请求会尝试上传最多 8 MiB 的数据,由 InitialTransferSize 属性定义。 请注意,InitialTransferSize 仅在使用可搜索流时适用于上传。 如果 blob 大小小于 8 MiB,则只需一个请求即可完成操作。 如果 blob 大小大于 8 MiB,则所有后续传输请求的最大大小为 4 MiB,我们使用 MaximumTransferSize 属性设置该大小。

上传的性能注意事项

在上传期间,存储客户端库会根据 StorageTransferOptions 实例中定义的值将给定上传流拆分为多个子上传。 每个子上传都有自己的对 REST 操作的专用调用。 对于 BlobClient 对象或 BlockBlobClient 对象,此操作是放置块。 对于 DataLakeFileClient 对象,此操作是追加数据。 存储客户端库并行管理这些 REST 操作(取决于传输选项),以完成完整上传。

根据上传流是可查找还是不可查找,客户端库会以不同的方式处理缓冲和 InitialTransferSize,如以下部分所述。 可搜索流是支持查询和修改流中当前位置的流。 若要详细了解 .NET 中的流,请参阅 Stream 类参考。

注意

块 blob 的最大块计数为 50,000 个块。 那么,块 blob 的最大大小是 MaximumTransferSize 的 50,000 倍。

上传期间的缓冲

存储 REST 层不支持从你中断的地方继续执行 REST 上传操作;个别传输要么完成,要么丢失。 为了确保不可搜索流上传的复原能力,存储客户端库会在开始上传之前为每个单独的 REST 调用缓冲数据。 除了网络速度限制之外,这种缓冲行为也是考虑较小的 MaximumTransferSize 值的原因,即使在按顺序上传时也是如此。 减小 MaximumTransferSize 的值会减少每个请求和每次重试失败请求时缓冲的最大数据量。 如果在特定大小的数据传输期间遇到频繁超时,则减小 MaximumTransferSize 的值会减少缓冲时间,并可能提高性能。

发生缓冲的另一种情况是当你使用并行 REST 调用来上传数据以最大化网络吞吐量时。 客户端库需要可以从中并行读取的源,并且由于流是顺序的,因此存储客户端库会在开始上传之前缓冲每个单独 REST 调用的数据。 即使提供的流是可供搜索的,也会发生这种缓冲行为。

为避免在异步上传调用期间发生缓冲,你必须提供可搜索流并将 MaximumConcurrency 设置为 1。 虽然此策略在大多数情况下都适用,但如果你的代码使用其他需要缓冲的客户端库功能,缓冲仍有可能发生。

上传时的 InitialTransferSize

提供了可搜索的流进行上传时,系统会根据 InitialTransferSize 的值检查流长度。 如果流长度小于此值,则整个流会作为单个 REST 调用上传,而不考虑其他 StorageTransferOptions 值。 否则,如前所述,上传会分多个部分完成。 InitialTransferSize 对不可查找的流没有影响,因此会被忽略。

下载的性能注意事项

在下载期间,存储客户端库会根据 StorageTransferOptions 实例中定义的值将给定下载流拆分为多个子下载。 每个子下载都有自己的对 REST 操作的专用调用。 根据传输选项,客户端库会并行管理这些 REST 操作,以完成完整下载。

下载期间的缓冲

同时接收多个 HTTP 响应和正文内容对内存使用有影响。 但是,存储客户端库不会为下载的内容显式添加缓冲步骤。 传入响应按顺序处理。 客户端库配置一个 16 KB 的缓冲区,用于将流从 HTTP 响应流复制到调用者提供的目标流或文件路径。

下载时的 InitialTransferSize

在下载过程中,存储客户端库会使用 InitialTransferSize 发出一个下载范围请求,然后再执行任何其他操作。 在此初始下载请求期间,客户端库知道资源的总大小。 如果初始请求成功下载了所有内容,则操作完成。 否则,客户端库会继续发出最多 MaximumTransferSize 的范围请求,直到完整下载完成。

后续步骤