PAYATHON 2026

Azure Blob Storage 多用户服务如何划分 container、使用 lease 和前缀分页?

数据小陈

结论

不必强制将所有用户的数据放在同一个 container。可以共享 container,并通过 blob 名称前缀区分用户;也可以为每个用户创建独立的 container。选择哪种方式取决于访问控制和管理需求。Azure 多租户存储指南介绍了这些隔离方式。

container lease 只保护 container 的删除操作,不会自动锁定其中所有 blob 的读写。因此,获取 container lease 不会让共享 container 中所有用户的文件操作等待。Lease Container 文档

原生 blob lease 针对单个 blob,没有一次性对任意 blob 列表获取原子租约的接口。列表查询支持服务端 prefix 过滤,过滤后的结果仍可能需要分页。

container 应该如何划分?

如果所有存储访问都经过后端,且后端负责用户鉴权,可以考虑共享 container,并使用这样的 blob 名称:

users/user-123/avatar.png
users/user-123/documents/report.pdf
users/user-456/avatar.png

这样可以通过 users/user-123/ 查询某个用户的数据。名称前缀本身不会形成权限隔离,后端必须根据已认证的用户生成允许访问的路径,不能直接相信客户端提交的用户 ID 或完整 blob 名称。

如果需要向客户端发放用户范围的 container SAS,或希望按用户管理 container,为每个用户创建独立的 container 更容易界定这个边界。同一 storage account 下拆分多个 container,并不意味着获得独立的性能配额。隔离模型与访问控制说明

选择时可以先明确两个问题:用户是否直接访问 Blob Storage,以及权限是否需要以 container 为边界。不要为了“锁住某个用户全部文件”而拆分 container,因为 container lease 没有这种强制效果。

container lease 和 blob lease 分别锁什么?

问题提到的基础代码只负责列出文件,没有提供具体实现或日志。客户端同时提供了 container lease 和 blob lease,容易让人把它们理解为不同层级的文件锁。实际上,它们保护的操作不同:

租约对象服务端强制约束的范围
container删除该 container 时需要有效的 lease ID
单个 blob对该 blob 的写入和删除操作需要有效的 lease ID

对已有有效 lease 的 blob 执行受保护的写入或删除时,请求必须携带对应的 lease ID。普通读取和列举通常不需要该 ID,因此 blob lease 不会阻止所有访问。Lease Blob 文档

即使每个用户都有独立 container,获取 container lease 也不会自动阻止其他进程修改该用户的 blob。

能否锁住指定的一组 blob?

Azure 的 Lease Blob 请求针对单个 blob。要保护多个 blob,可以分别获取各自的 lease,但这些请求不会组成一个“全部成功或全部失败”的原子操作。Lease Blob 请求定义

如果业务需要逐个获取租约,应用层至少要处理以下事项:

  1. 按固定顺序获取各个 blob 的 lease,降低并发竞争造成相互等待的风险。
  2. 中途获取失败时,释放已经获取的 lease。
  3. 执行受保护的操作时,为每个 blob 携带各自的 lease ID。
  4. 操作结束后释放租约;使用有限时长租约时,还要处理续租和租约失效。

这仍然不会让多个 blob 的修改成为事务。如果部分写入成功、后续写入失败,业务需要自行处理恢复或补偿。

如果只是想让同一用户的后台任务串行执行,可以考虑为每个用户创建一个专用的协调 blob,让任务先获取它的 lease。这是一项应用层约定:所有任务都必须遵守,且失去租约后必须停止后续工作。协调 blob 的 lease 不会自动保护其他数据 blob。

如何按前缀查询并分页?

可以直接在 List Blobs 请求中传入 prefix。例如,要列出 users/user-123/ 下的 blob,请求路径可以写成:

GET /shared-data?restype=container&comp=list&prefix=users%2Fuser-123%2F&maxresults=100 HTTP/1.1
Host: exampleaccount.blob.core.windows.net

这是请求路径示意,实际调用还需补齐认证及所用 API 要求的请求头。shared-data 是示例 container,maxresults=100 是示例分页大小。

服务端会按前缀过滤结果,不需要先下载整个 container 的列表。建议保留用户 ID 后的 /,避免使用 users/user-1 时同时匹配 users/user-10。

前缀过滤后仍需处理分页:

  • prefix 限定返回哪些名称。
  • maxresults 限定单页最多返回多少项。
  • 响应中的 NextMarker 用于继续请求下一页。

请求下一页时,继续使用相同的前缀,并将返回的 NextMarker 作为 marker 参数传回。放入 URL 时要进行编码:

GET /shared-data?restype=container&comp=list&prefix=users%2Fuser-123%2F&maxresults=100&marker=<URL编码后的NextMarker> HTTP/1.1
Host: exampleaccount.blob.core.windows.net

NextMarker 应作为不透明值处理,不要解析或自行构造。即使某一页少于 maxresults,也可能仍有后续结果;应持续读取,直到 NextMarker 为空。REST API 默认单页最多返回 5,000 项,这不是 container 可以存储的 blob 总数限制。List Blobs 参数与分页规则

使用 azure_storage_blobs 时如何落地?

问题没有提供 crate 的具体版本,因此无法保证某一套 Rust 方法签名适用。实现时应对照项目锁定版本的文档,确认列表请求如何设置前缀,以及分页流是否会自动请求后续页面。

可以按以下顺序修改现有服务:

  1. 确定采用共享 container 还是每用户独立 container,并落实访问控制。
  2. 将“列出全部文件再筛选”改为服务端前缀查询。
  3. 从一开始就处理分页,逐页处理结果,避免遗漏或一次性积累全部列表。
  4. 单独设计并发控制:单个 blob 的写入保护使用 blob lease,用户任务串行化使用明确的协调机制。

所用 crate 的弃用和替代情况属于 SDK 选型问题,不会改变上述 Azure 服务端 lease 和列表查询的语义。

备注:内容仅供参考。