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

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

本文将带你详细了解在优化数据传输选项时的几点注意事项。 正确优化后,客户端库可以有效地跨多个请求分发数据,从而提高操作速度、内存使用率和网络稳定性。

针对上传进行性能优化

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

设置上传的传输选项

可根据应用需求优化以下参数:

  • max_single_put_size:通过单个请求上传的 blob 的最大大小。 默认值为 64 MiB。
  • max_block_size:当以区块上传块 blob 时,传输的最大长度(以字节为单位)。 默认值为 4 MiB。
  • max_concurrency:可以并行使用的最大子传输数。

注意

对于未提供的数据传输选项,客户端库将会使用默认值。 这些默认值通常在数据中心环境中表现良好,但不太可能适合家庭使用者环境。 数据传输优化不当可能会导致操作时间过长,甚至请求超时。 最好主动测试这些值,并根据应用程序和环境的需求优化它们。

max_single_put_size

参数“max_single_put_size”表示单个请求上传最大的 blob 大小(以字节为单位)。 如果 blob 大小小于或等于 max_single_put_size,则通过单个放置 Blob 请求上传 blob。 如果 blob 大小大于 max_single_put_size,或者 blob 大小未知,则使用一系列放置块调用(后跟放置块列表)以区块形式上传 blob。

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

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

max_block_size

参数“max_block_size”规定当以区块上传块 blob 时,传输的最大长度(以字节为单位)。 如前所述,此值不限制 max_single_put_size,后者可以大于 max_block_size

为了保持数据高效移动,客户端库可能不会每次传输都达到 max_block_size 值。 根据操作的不同,传输大小的最大支持值可能会有所不同。 若要详细了解 Blob 存储的传输大小限制,请参阅 Blob 存储的缩放目标中的图表。

代码示例

以下代码示例演示如何在创建 BlobClient 对象时指定数据传输选项,以及如何使用该客户端对象上传数据。 此示例中提供的值不作为建议。 若要正确优化这些值,需要考虑应用的特定需求。

def upload_blob_transfer_options(self, account_url: str, container_name: str, blob_name: str):
    # Create a BlobClient object with data transfer options for upload
    blob_client = BlobClient(
        account_url=account_url, 
        container_name=container_name, 
        blob_name=blob_name,
        credential=DefaultAzureCredential(),
        max_block_size=1024*1024*4, # 4 MiB
        max_single_put_size=1024*1024*8 # 8 MiB
    )
    
    with open(file=os.path.join(r'file_path', blob_name), mode="rb") as data:
        blob_client = blob_client.upload_blob(data=data, overwrite=True, max_concurrency=2)

在此示例中,我们使用方法调用上的参数“max_concurrency”将并行传输辅助角色的数量设置为 2。 此配置最多可同时打开 2 个连接,从而允许并行上传。 在安装客户端的过程中,我们将参数“max_single_put_size”设置为 8 MiB。 如果 blob 大小小于 8 MiB,则只需一个请求即可完成上传操作。 如果 blob 大小大于 8 MiB,则按参数“max_block_size”设置的最大区块大小为 4 MiB 的区块上传 blob。

上传的性能注意事项

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

可以在以下部分了解客户端库如何处理缓冲。

注意

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

上传期间的缓冲

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

默认情况下,SDK 会为每个并发子负载请求缓冲字节数据 max_block_size,但如果满足以下条件,则每个请求的内存使用量可以限制为 4 MiB:

  • 参数“max_block_size”必须大于 min_large_block_upload_threshold。 可在客户端实例化期间定义参数“min_large_block_upload_threshold”,规定使用内存高效算法所需的最小区块大小(以字节为单位)。 参数“min_large_block_upload_threshold”默认为“4*1024*1024 + 1”。
  • 提供的流必须是可查找的。 可搜索流是支持查询和修改流中当前位置的流。
  • blob 必须是块 blob。

虽然此策略在大多数情况下都适用,但如果你的代码使用其他需要缓冲的客户端库功能,更多缓冲仍有可能发生。

针对下载进行性能优化

合理优化数据传输选项是确保可靠的下载性能的关键。 根据这些参数中的值,将存储传输划分为多个子传输。

设置下载的传输选项

可根据应用需求优化以下参数:

  • max_chunk_get_size:用于下载 blob 的最大区块大小。 默认值为 4 MiB。
  • max_concurrency:可以并行使用的最大子传输数。
  • max_single_get_size:要在单个调用中下载的 blob 的最大大小。 如果 blob 总大小超过 max_single_get_size,将分区块下载其余 blob 数据。 默认值为 32 MiB。

代码示例

def download_blob_transfer_options(self, account_url: str, container_name: str, blob_name: str):
    # Create a BlobClient object with data transfer options for download
    blob_client = BlobClient(
        account_url=account_url, 
        container_name=container_name, 
        blob_name=blob_name,
        credential=DefaultAzureCredential(),
        max_single_get_size=1024*1024*32, # 32 MiB
        max_chunk_get_size=1024*1024*4 # 4 MiB
    )

    with open(file=os.path.join(r'file_path', 'file_name'), mode="wb") as sample_blob:
        download_stream = blob_client.download_blob(max_concurrency=2)
        sample_blob.write(download_stream.readall())

下载的性能注意事项

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

下载的 max_single_get_size 参数

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

后续步骤