容器分片

容器分片是运营商控制的功能,可用于将非常大的容器数据库分成多个较小的分片容器

注意

强烈建议运营商在生产环境中使用之前,在非生产集群中获得容器分片的经验。

分片过程涉及通过容器复制引擎移动所有分片容器数据库记录;完成分片所需的时间取决于现有的集群负载以及正在分片的容器数据库的性能。

目前没有记录在案的流程来撤销在启用分片后的分片过程。

背景

Swift 中每个容器的元数据存储在 SQLite 数据库中。此元数据包括:有关容器的信息,例如其名称、修改时间和当前对象计数;客户端可能写入容器的用户元数据;容器中每个对象的记录。容器数据库对象记录用于响应容器 GET 请求生成容器列表;每个对象记录存储对象名称、大小、哈希和内容类型以及相关时间戳。

随着容器中对象数量的增加,容器数据库中对象记录的数量也会增加。最终容器数据库性能开始下降,更新对象记录所需的时间增加。这可能导致对象更新超时,从而导致待处理的 异步更新 数量增加,对象服务器上出现相应数量的增加。容器数据库通常在多个节点上复制,任何数据库性能下降也可能导致更长的 容器复制 时间。

容器数据库性能开始下降的时间取决于容器环中硬件的选择。经验证据表明,对象记录达到数千万的容器性能明显下降。

可以通过确保客户端使用将对象分散到多个容器的对象命名方案来避免这种性能下降,从而在多个容器数据库之间分配负载。但是,这并不总是可取的,也不在集群运营商的控制范围内。

Swift 的容器分片功能为运营商提供了一种机制,可以将单个客户端可见容器上的负载分布到多个隐藏的分片容器上,每个分片容器存储容器对象记录的子集。客户端不知道容器分片;客户端继续使用相同的 API 来访问容器,如果已分片,则该容器映射到 Swift 集群中的多个分片容器。

部署和操作

升级注意事项

在尝试分片容器之前,必须将 Swift 集群中的所有服务器升级以支持容器分片功能。

识别需要分片的容器

容器分片当前由 swift-manage-shard-ranges CLI 工具 如下所述 启动。运营商必须首先识别作为分片候选者的容器。为了协助完成此操作,容器分片守护进程 检查其访问的容器的大小,并将分片候选者列表写入 recon 缓存。例如

"sharding_candidates": {
    "found": 1,
    "top": [
        {
            "account": "AUTH_test",
            "container": "c1",
            "file_size": 497763328,
            "meta_timestamp": "1525346445.31161",
            "node_index": 2,
            "object_count": 3349028,
            "path": <path_to_db>,
            "root": "AUTH_test/c1"
        }
    ]
}

如果容器的对象计数大于或等于 shard_container_threshold 选项,则认为该容器是分片候选者。报告的候选者数量限制为由 recon_candidates_limit 选项配置的数量,以便仅将最大的候选容器包含在 sharding_candidates 数据中。

swift-manage-shard-ranges CLI 工具

swift-manage-shard-ranges 工具提供用于启动容器分片的命令。swift-manage-shard-ranges 直接在容器数据库文件上操作。

注意

swift-manage-shard-ranges 只能在容器数据库的一个副本上使用,以避免不一致的结果。 swift-manage-shard-ranges 所做的修改将通过正常的复制过程自动复制到容器数据库的其他副本。

启动分片的过程包含三个步骤,每个步骤都可以单独执行,或者如下所示,使用单个命令执行。

  1. find 子命令扫描容器数据库以确定需要多少个分片容器以及它们将管理哪些对象。每个分片容器管理由 lowerupper 边界定义的对象命名空间的范围。分配给每个分片容器的最大对象数量在命令行中指定。例如

    $ swift-manage-shard-ranges <path_to_db> find 500000
    Loaded db broker for AUTH_test/c1.
    [
      {
        "index": 0,
        "lower": "",
        "object_count": 500000,
        "upper": "o_01086834"
      },
      {
        "index": 1,
        "lower": "o_01086834",
        "object_count": 500000,
        "upper": "o_01586834"
      },
      {
        "index": 2,
        "lower": "o_01586834",
        "object_count": 500000,
        "upper": "o_02087570"
      },
      {
        "index": 3,
        "lower": "o_02087570",
        "object_count": 500000,
        "upper": "o_02587572"
      },
      {
        "index": 4,
        "lower": "o_02587572",
        "object_count": 500000,
        "upper": "o_03087572"
      },
      {
        "index": 5,
        "lower": "o_03087572",
        "object_count": 500000,
        "upper": "o_03587572"
      },
      {
        "index": 6,
        "lower": "o_03587572",
        "object_count": 349194,
        "upper": ""
      }
    ]
    Found 7 ranges in 4.37222s (total object count 3349194)
    

    此命令返回一个分片范围列表,其中每个分片范围描述了分片容器要管理的命名空间。此命令不执行任何其他操作,并且容器数据库未更改。可以将输出重定向到文件,以便稍后由 replace 命令检索。例如

    $ swift-manage-shard-ranges <path_to_db> find 500000 > my_shard_ranges
    Loaded db broker for AUTH_test/c1.
    Found 7 ranges in 2.448s (total object count 3349194)
    
  2. replace 子命令删除容器数据库中可能已存在的任何分片范围,并从给定文件插入分片范围。文件内容应采用 find 子命令生成的文件格式。例如

    $ swift-manage-shard-ranges <path_to_db> replace my_shard_ranges
    Loaded db broker for AUTH_test/c1.
    No shard ranges found to delete.
    Injected 7 shard ranges.
    Run container-replicator to replicate them to other nodes.
    Use the enable sub-command to enable sharding.
    

    容器数据库已修改为存储分片范围,但容器不会开始分片,直到启用分片。可以使用 info 子命令在任何时候检查容器数据库的状态,并可以使用 show 子命令显示插入的分片范围。

    可以使用 replace 子命令替换容器数据库中存储的分片范围。这将首先删除所有现有的分片范围,然后再存储新的分片范围。还可以使用 delete 子命令从容器数据库中删除分片范围。

    在采取启用分片的下一步之后,不应使用 swift-manage-shard-ranges 替换或删除分片范围。

  3. enable 子命令启用容器进行分片。分片守护进程和/或容器复制守护进程会将分片范围复制到容器 DB 的其他副本,并且分片守护进程将继续分片容器。此过程可能需要一些时间,具体取决于容器的大小、分片范围的数量和底层硬件。

    注意

    一旦使用 enable 子命令,就没有支持的机制来撤销分片。不要使用 swift-manage-shard-ranges 对容器 DB 中的分片范围进行任何进一步的更改。

    例如

    $ swift-manage-shard-ranges <path_to_db> enable
    Loaded db broker for AUTH_test/c1.
    Container moved to state 'sharding' with epoch 1525345093.22908.
    Run container-sharder on all nodes to shard the container.
    

    这不会分片容器 - 分片是由 容器分片守护进程 执行的 - 但设置了守护进程随后开始分片过程所需的数据库状态。

    输出中显示的 epoch 值是启用分片的时间。当 容器分片守护进程 开始分片此容器时,它将使用文件名中的 epoch 创建一个新的容器数据库文件,以与正在分片的回退 DB 区分开来。

所有三个步骤都可以使用一个子命令执行

$ swift-manage-shard-ranges <path_to_db> find_and_replace 500000 --enable --force
Loaded db broker for AUTH_test/c1.
No shard ranges found to delete.
Injected 7 shard ranges.
Run container-replicator to replicate them to other nodes.
Container moved to state 'sharding' with epoch 1525345669.46153.
Run container-sharder on all nodes to shard the container.
exception swift.cli.manage_shard_ranges.GapsFoundException

基础: ManageShardRangesException

exception swift.cli.manage_shard_ranges.InvalidSolutionException(msg, acceptor_path, overlapping_donors)

基础: ManageShardRangesException

exception swift.cli.manage_shard_ranges.InvalidStateException

基础: ManageShardRangesException

exception swift.cli.manage_shard_ranges.ManageShardRangesException

基础: Exception

swift.cli.manage_shard_ranges.wrap_for_argparse(func, msg=None)

包装给定的 func 以捕获任何 ValueError 并引发 argparse.ArgumentTypeError 代替。

参数:
  • func – 一个函数。

  • msg – 用于任何使用的异常的可选消息;如果未提供,则将使用 ValueError 的字符串表示形式。

返回值:

一个函数包装器。

容器分片守护进程

一旦为容器启用了分片,分片操作由 容器分片守护进程 执行。 容器分片守护进程 守护进程必须在所有容器服务器上运行。 container-sharder 守护进程会定期访问每个容器数据库以执行所需的任何容器分片任务。

container-sharder 守护进程需要在容器服务器配置文件中存在 [container-sharder] 配置部分;示例配置部分显示在 container-server.conf-sample 文件中。

注意

auto_shard 选项当前推荐用于生产系统,应设置为 false(默认值)。

几个 [container-sharder] 配置选项只有在启用 auto_shard 选项时才重要。此选项使 container-sharder 守护进程能够自动识别作为分片候选者的容器并启动分片过程,而不是使用 swift-manage-shard-ranges 工具。

容器分片守护进程使用内部客户端,因此需要存在内部客户端配置文件。默认情况下,预计内部客户端配置文件位于 /etc/swift/internal-client.conf。可以使用 [container-sharder] 配置部分中的 internal_client_conf_path 选项指定配置文件的替代位置。

内部客户端配置文件的内容应与 internal-client.conf-sample 文件相同。特别是,内部客户端配置应具有

account_autocreate = True

[proxy-server] 部分中。

容器数据库可能需要由 container-sharder 守护进程多次访问才能完全分片。每次访问时,container-sharder 守护进程会将一部分对象记录移动到新的分片容器,通过从原始容器中分离新的分片容器数据库来实现。默认情况下,每次处理两个分片;可以通过 cleave_batch_size 选项配置此数字。

container-sharder 守护进程会定期将正在分片的容器的进度数据写入 recon 缓存。例如

"sharding_in_progress": {
    "all": [
        {
            "account": "AUTH_test",
            "active": 0,
            "cleaved": 2,
            "container": "c1",
            "created": 5,
            "db_state": "sharding",
            "error": null,
            "file_size": 26624,
            "found": 0,
            "meta_timestamp": "1525349617.46235",
            "node_index": 1,
            "object_count": 3349030,
            "path": <path_to_db>,
            "root": "AUTH_test/c1",
            "state": "sharding"
        }
    ]
}

此示例指示,在总共 7 个分片范围中,已分离 2 个,而 5 个仍处于创建状态,等待分离。

分片容器是在内部帐户中创建的,对客户端不可见。默认情况下,帐户 AUTH_test 的分片容器是在内部帐户 .shards_AUTH_test 中创建的。

一旦容器开始分片,对该容器的对象更新可能会被重定向到分片容器。 container-sharder 守护进程还负责将分片的对象计数和已用字节数更新发送到原始容器,以便可以在响应客户端请求时返回聚合对象计数和已用字节数的值。

注意

为了生成分片对象统计信息更新,container-sharder 守护进程必须在所有容器服务器上继续运行。

底层实现

术语

名称

描述

根容器

存在于用户帐户中的原始容器。它保存对其分片容器的引用。

待淘汰的数据库

要进行分片的原始数据库文件。

新的数据库

将替换待淘汰数据库的数据库文件。

纪元 (Epoch)

创建新的数据库的时间戳;纪元值嵌入在新的数据库文件名中。

分片范围

由下限和上限定义的对象命名空间的范围。

分片容器

保存分片范围对象记录的容器。分片容器存在于隐藏的帐户中,镜像用户的帐户。

父容器

从其分离出分片容器的容器。首次分片根容器时,每个分片的父容器将是根容器。分片分片容器时,每个分片的父容器将是正在分片的分片容器。

放置错误的的对象

不属于容器分片范围的项目。这些将被 container-sharder 移动到正确的位置。

分离 (Cleaving)

将分片范围内的对象记录移动到分片容器数据库的行为。

收缩 (Shrinking)

将小的分片容器合并到另一个分片容器中,以删除小的分片容器的行为。

捐赠者 (Donor)

正在收缩的分片范围。

接受者 (Acceptor)

捐赠者合并到的分片范围。

查找分片范围

分片容器的最终目标是用许多分片容器数据库替换原始容器数据库,每个分片容器数据库负责存储整个对象命名空间的范围。实现这一目标的第一步是识别一组合适的连续对象命名空间,称为分片范围,每个分片范围包含容器当前对象内容大小相似的部分。

由于对象名称不保证均匀分布,因此不能简单地通过均匀分片命名空间来选择分片范围。如果容器被简单地分成两个分片范围,一个包含所有对象名称直到 m,另一个包含所有对象名称超过 m,那么如果所有对象名称实际上都以 o 开头,结果将是不平衡的分片容器对。

假设每个需要分片的容器都可以分成两个也是过于简单的。这可能是理想世界的目标,但在实践中,会有一些容器已经变得非常大,应该分成许多分片。此外,在大型 SQLite 数据库中查找现有对象名称的精确中点所需的时间将随着容器大小的增加而增加。

出于这些原因,大小为 N 的分片范围是通过在数据库表中按对象名称排序搜索第 N 个对象,然后搜索第 (2 * N) 个对象,依此类推,直到搜索完所有对象来找到的。对于正好有 2N 个对象的容器,最终结果与在对象名称的中点处分片容器相同。在实践中,通常会为具有大于 2N 个对象和多个分片范围的容器启用分片,最后一个分片可能包含少于 N 个对象。对于具有 N 的大倍数个对象的容器,可以批量识别分片范围,从而实现更具可扩展性的解决方案。

为了说明这个过程,考虑一个用户帐户 acct 中的一个非常大的容器,它是分片候选对象

_images/sharding_unsharded.svg

swift-manage-shard-ranges CLI 工具 工具的 find 子命令在对象表中搜索第 N 个对象,其名称将成为第一个分片范围的上限,也是第二个分片范围的下限。第一个分片范围的下限是空字符串。

为了本示例的目的,第一个上限是 cat

_images/sharding_scan_basic.svg

swift-manage-shard-ranges CLI 工具 继续搜索容器以查找进一步的分片范围,最终上限也是空字符串。

启用分片

一旦找到分片范围,swift-manage-shard-ranges CLI 工具replace 子命令用于将它们插入到容器数据库的 shard_ranges 表中。除了其下限和上限之外,每个分片范围都被赋予一个唯一的名称。

然后,enable 子命令创建一些最终状态,以启动容器的分片,包括一个特殊的分片范围记录,称为容器的 own_shard_range,其名称等于容器的路径。这用于记录容器覆盖的对象命名空间,对于用户容器始终是整个命名空间。只有当其 own shard range 的状态设置为 SHARDING 时,才会开始分片容器。

ShardRange

ShardRange 类提供用于与分片范围的属性和状态交互的方法。该类封装了以下属性

  • 分片范围的名称,它也是用于保存其命名空间中对象记录的分片容器的名称。

  • 定义分片范围对象命名空间的下限和上限。

  • 一个已删除的标志。

  • 上次修改边界和已删除标志的时间戳。

  • 分片范围的对象统计信息,即对象计数和已用字节数。

  • 上次修改对象统计信息的时间戳。

  • 分片范围的状态和纪元,纪元是分片容器数据库文件名中使用的的时间戳。

  • 上次修改状态和纪元的时间戳。

分片范围经历以下状态

  • FOUND:已识别出要分片的容器中的分片范围,但尚未为其创建任何资源。

  • CREATED:已创建分片容器以存储分片范围的内容。

  • CLEAVED:分片容器的内容已从分片容器的至少一个副本复制到分片容器。

  • ACTIVE:当分片容器中的所有分片范围都已分离时,分片容器的组成分片范围将移动到此状态。

  • SHRINKING:已启用分片范围的收缩;或者

  • SHARDING:已启用分片范围,以进一步分片。

  • SHARDED:分片范围已完成分片或收缩;容器现在通常将具有多个组成 ACTIVE 分片范围。

注意

分片范围状态表示在容器的任何副本上的分片范围的最先进状态。例如,处于 CLEAVED 状态的分片范围可能尚未在所有副本上完成分离,但已在一个副本上完成分离。

新的和待淘汰的数据库文件

如前所述,写入大型容器会导致容器服务器的延迟增加。一旦在容器上启动了分片,就希望停止写入大型数据库;最终它将被取消链接。这主要通过将对象更新重定向到创建的新分片容器来实现(参见 重定向对象更新 下方),但仍可能需要由根容器接受一些对象更新,并且仍必须修改其他容器元数据。

为了使大型 待淘汰 数据库有效地变为只读,当 container-sharder 守护进程 找到具有一组分片范围记录(包括 own_shard_range)的容器时,它首先创建一个新的数据库文件,该文件最终将替换现有的 待淘汰 数据库。对于一个文件名是

<hash>.db

新的数据库文件名形式为

<hash>_<epoch>.db

其中 epoch 是存储在容器的 own_shard_range 中的时间戳。

新的数据库具有待淘汰数据库的 shard_ranges 表以及所有其他容器元数据,但不包括对象记录。创建新的数据库文件后,它将用于存储任何新的对象更新,并且不再将对象记录写入待淘汰数据库文件。

一旦分片过程完成,待淘汰的数据库文件将被取消链接,只留下新的数据库文件在容器的目录中。因此,容器 DB 目录可能处于三种状态:UNSHARDED、SHARDING 和 SHARDED。

_images/sharding_db_states.svg

如果容器缩小到没有分片,则新的数据库开始存储对象记录,行为与未分片的容器相同。这被称为 COLLAPSED 状态。

总而言之,任何容器副本可能处于的 DB 状态是

  • UNSHARDED - 在此状态下,只有一个标准的容器数据库。所有容器最初都处于此状态。

  • SHARDING - 现在有两个数据库,待淘汰的数据库和一个新的数据库。新的数据库存储任何元数据、容器级别统计信息以及一个对象持有表,以及一个存储分片范围的表。

  • SHARDED - 只有一个数据库,即新的数据库,它具有一个或多个分片范围以及自己的分片范围。待淘汰的数据库已被取消链接。

  • COLLAPSED - 只有一个数据库,即新的数据库,它只有自己的分片范围并存储对象记录。

注意

DB 状态对于容器的每个副本是唯一的,并且不一定与分片范围状态同步。

创建分片容器

container-sharder 守护进程 接下来使用分片范围名称作为分片容器的名称创建每个分片范围的分片容器

_images/sharding_cleave_basic.svg

每个分片容器都有一个 own_shard_range 记录,其中包含其负责的对象命名空间的下限和上限,以及对分片用户容器的引用,该容器称为 root_container。与 root_container 不同,分片容器的 own_shard_range 不涵盖整个命名空间。

分片范围名称的形式为 <shard_a>/<shard_c>,其中 <shard_a> 是一个隐藏的帐户,<shard_c> 是从根容器派生的容器名称。

用于分片容器的帐户名称 <shard_a> 通过在用户帐户前加上字符串 .shards_ 来形成。这避免了命名空间冲突,并将所有分片容器从帐户的用户视图中排除。

每个分片容器的容器名称的形式为

<root container name>-<hash of parent container>-<timestamp>-<shard index>

其中 root 容器名称 是属于分片容器内容的用户的容器名称,父容器 是正在从中分离分片的容器的名称,时间戳 是创建分片范围的时间,分片索引父容器 的名称排序分片范围列表中的位置。

分片用户容器时,父容器名称将与根容器相同。但是,如果分片容器增长到需要分片的大小,则其分片的父容器名称将是正在分片的分片容器的名称。

例如,考虑一个路径为 AUTH_user/c 的用户容器,它被分成两个分片容器,其名称将是

.shards_AUTH_user/c-<hash(c)>-1234512345.12345-0
.shards_AUTH_user/c-<hash(c)>-1234512345.12345-1

如果第一个分片容器随后被分成进一步的两个分片容器,则它们将被命名为

.shards_AUTH_user/c-<hash(c-<hash(c)>-1234567890.12345-0)>-1234567890.12345-0
.shards_AUTH_user/c-<hash(c-<hash(c)>-1234567890.12345-0)>-1234567890.12345-1

这种命名方案保证了分片和分片的碎片都具有唯一且长度受限的名称。

分离分片容器

创建空分片容器后,sharder 守护进程将继续从待淘汰的数据库中将对象分离到每个分片范围。分离按默认的两个分片范围批次进行,因此,如果容器具有两个以上分片范围,则守护进程必须多次访问它才能完成分离。

要分离分片范围,守护进程在本地设备上为分片容器创建一个分片数据库。该设备可能是分片容器的其中一个主节点,但通常不是。然后,来自相应分片范围命名空间的对象记录从待淘汰的数据库复制到此分片数据库。

Swift 的容器复制机制随后被用于将其分片 DB 复制到其主节点。会进行检查以确保新的分片容器 DB 已被复制到足够数量的主节点,然后才认为它已成功分离。默认情况下,守护进程需要成功复制一个新的分片代理到容器环复制计数的一个法定数量,但可以使用 shard_replication_quorum 选项来调整此要求。

一旦分片范围已成功从要退役的数据库中分离,守护进程将其状态转换为 CLEAVED。需要注意的是,只要其中一个要退役的 DB 副本已分离了分片范围,就会发生此状态转换,因此并不意味着所有要退役的 DB 副本都已分离该范围。状态转换的意义在于,该分片容器现在被认为适合为对象列表做出贡献,因为其内容存在于其主节点的法定数量中,并且与该命名空间的至少一个要退役的 DB 相同。

一旦分片范围处于 CLEAVED 状态,可以可选地放宽对其他要退役的 DB 实例的“成功”分离的要求,因为不需要立即将其内容复制到其主节点。可以使用 existing_shard_replication_quorum 选项来减少分片守护程序认为分离的分片范围需要成功复制的法定数量。

注意

一旦分离,分片容器 DB 将继续由正常的 container-replicator 守护程序复制,以便最终完全复制到所有主节点,而不管分片守护程序使用的任何复制法定数量选项如何。

必须独立跟踪要退役的 DB 的每个副本的分离进度,与分片范围的状态分开。这是使用与关联的要退役的 DB 维护分离游标的 per-DB CleavingContext 对象来完成的。分离游标只是从该特定要退役的 DB 分离的最后一个分片范围的上限。

每个 CleavingContext 存储在分片容器的 sysmeta 中,其键是退役 DB 的 id。由于所有容器 DB 文件都有唯一的 id,这保证了每个要退役的 DB 都有一个唯一的 CleavingContext。此外,如果退役 DB 文件发生更改,例如通过 rsync_then_merge 复制操作,该操作可能会更改 DB 的对象表的内容,那么它将获得一个新的唯一的 CleavingContext。

CleavingContext 维护其他状态,用于确保只有当其所有对象行都已分离到分片范围时,才认为要退役的 DB 已完全分离并准备好被删除。

一旦所有分片范围都已从要退役的 DB 中分离,它将被删除。容器现在由新的 DB 表示,该 DB 具有指向存储容器对象记录的分片容器的分片范围记录表。

重定向对象更新

一旦存在分片容器,来自新客户端请求和异步待处理文件的对象更新将被重定向到分片容器,而不是根容器。这减轻了根容器的负载。

对于分片(或部分分片)容器,当代理收到新的对象请求时,它会向容器发出 GET 请求,以获取描述应该将对象更新发送到的分片容器的数据。然后,代理会将分片容器位置注释到对象请求中,以便对象服务器将对象更新转发到分片容器。如果这些更新失败,则写入对象服务器的异步待处理文件包含分片容器位置。

当对象更新器处理先前失败的对象更新的异步待处理文件时,它可能找不到分片容器位置。在这种情况下,更新器会将更新发送到 root container,后者会返回一个包含分片容器位置的重定向响应。

注意

对象更新会在分片容器存在时立即重定向,即使要退役的 DB 对象记录尚未分离到分片容器。这可以防止进一步写入要退役的 DB,并避免新的对象更新污染新的 DB。目标是最终将所有对象记录都放在分片容器中,而不是根容器中。

构建容器列表

通过查询分片容器以获取列表的组件来处理分片容器的列表请求。代理将客户端列表请求转发到根容器,就像转发到未分片的容器一样,但容器服务器会响应一个分片范围列表,而不是对象。然后,代理会按命名空间顺序查询每个分片容器以获取其列表,直到达到列表长度限制或列出所有分片范围为止。

当容器仍在分片过程中时,只有已分离的分片范围才会在构建容器列表时使用。尚未分离的分片范围将不会有任何来自根容器的对象记录。根容器继续为未分离的部分命名空间提供列表。

注意

新的对象更新会被重定向到尚未分离的分片容器。因此,这些更新在分片范围分离之前不会包含在容器列表中。

示例请求重定向

例如,考虑一个分片容器,其中已找到 3 个分片范围,分别以 cat、giraffe 和 igloo 结尾。它们的各自的分片容器已被创建,因此最多到“igloo”的对象更新请求将被重定向到适当的分片容器。根 DB 继续处理任何对象名称超过“igloo”的列表请求和更新请求。

_images/sharding_scan_load.svg

分片守护程序从要退役的 DB 将对象分离到分片范围 DB;它还会将根容器的新 DB 中的任何错位对象移动到分片 DB。分离进度由蓝色线条表示。一旦第一个分片范围被分离,针对该命名空间的列表请求就会被重定向到分片容器。根容器仍然为剩余的命名空间提供列表。

_images/sharding_cleave1_load.svg

该过程继续:分片守护程序分离下一个范围,并找到一个新的范围,其上限为“linux”。现在,根容器只需要处理最多到“giraffe”的列表请求和名称大于“linux”的对象请求。根 DB 上的负载将继续减少,并分散到分片 DB 中。

_images/sharding_cleave2_load.svg

容器复制

分片范围记录在容器 DB 副本之间复制的方式与未分片容器的对象记录复制的方式大致相同。但是,一旦容器能够被分片,容器副本之间的常规对象记录复制就会停止。相反,对象记录将被移动到分片容器中的新位置。这避免了容器副本之间不必要的复制流量。

为了便于此,分片范围在复制期间会被“推送”和“拉取”,在尝试复制对象之前。这意味着发起复制的节点会在复制过程早期从目标节点了解分片范围,并且如果它发现它具有分片范围并且能够分片,则可以跳过对象复制。

注意

当容器复制的目标 DB 缺失时,仍然使用“complete_rsync”复制机制,在这种情况下,只有对象记录和分片范围记录才会被复制到目标节点。

容器删除

分片容器可以通过 DELETE 请求删除,就像未分片的容器一样。分片容器必须为空才能被删除,这意味着其所有分片容器都必须报告它们为空。

当根容器被删除时,分片容器不会立即被删除;分片容器保持未删除状态,以便它们能够继续接收可能在根容器删除后到达的对象更新。分片容器继续将其对象统计信息更新到已删除的根容器。如果分片容器收到导致它不再为空的对象更新,那么一旦该分片容器发送对象统计信息更新,根容器将不再被认为已删除。

分片分片容器

分片容器可能会增长到需要对其进行分片的大小。可以使用 swift-manage-shard-ranges 来识别分片容器内的分片范围并以与根容器相同的方式启用分片。当分片正在分片时,它会通知根容器其分片范围,以便根容器可以开始将对象更新重定向到新的“子分片”。当分片完成分片时,根容器会了解所有新的子分片,并且分片容器会删除根容器分片范围表中的其分片范围记录。此时,根容器会了解所有新的子分片,这些子分片共同覆盖了已删除分片的命名空间。

根容器和其直接分片之间没有分片的层次结构。当分片分片时,其子分片实际上会与根容器重新关联。

缩小分片容器

分片容器的内容可能会减少到不再需要分片容器的程度。如果发生这种情况,则可以将分片容器缩小到另一个分片范围。缩小以类似于分片的方式完成:将“接受器”分片范围写入缩小分片容器的分片范围表;与分片不同,其中分片范围涵盖分片容器命名空间的子集,接受器分片范围是缩小分片范围的超集。

一旦给定接受器分片范围,缩小分片就会将其分离到接受器,然后从根容器分片范围表中删除自身。