[译]谷歌文件系统(The Google File System)(6~9章)(完)

679 阅读26分钟

论文前半部分:[译]谷歌文件系统(The Google File System)(1~5章)

6. 量化测试

在本节中,我们将介绍一些小规模基准测试来说明GFS体系结构和实现中固有的瓶颈,以及Google使用的实际集群中的一些数值

6.1 微基准测试

我们在一个由一个Master服务器、两个Master副本节点、16个块服务器和16个客户端组成的GFS集群上进行性能测试。请注意,设置此配置是为了便于测试。典型的集群有数百个块服务器和数百个客户端

所有机器都配备了双1.4GHz PIII处理器、2GB内存、两个80GB 5400 rpm磁盘和一个100 Mbps的到HP 2524交换机的全双工以太网连接。所有19个GFS服务器机器都连接到一个交换机,所有16个客户端机器都连接到另一个交换机,这两个交换机之间通过1Gbps的链路连接

6.1.1 读取

N个客户端同时从文件系统读取数据,每个客户机从320GB文件集中随机读取4MB区域的内容。整个过程重复了256次,因此每个客户机最终读取1gb的数据。一台块服务器总共只有32GB的内存,因此我们预计Linux缓冲缓存的命中率最多为10%。我们的测试结果应该接近冷缓存结果

图3(a)显示了N个客户端的总读取速率及其理论限制。当两个交换机之间的1Gbps链路饱和时,限制峰值为125 MB/s,或当其100Mbps网络接口饱和时,每个客户端的限制峰值为12.5MB/s,以适用者为准。当只有一个客户端正在读取时,观察到的读取速率为10MB/s,即每个客户端限制的80%。对于16个客户机,总读取速率达到94mb/s,约为125mb/s链路限制的75%,也就是说每个客户端速率为6mb/s。效率从80%下降到75%的原因是随着客户端数量的增加,多个客户端同时从同一个块服务器读取的可能性也会增加

6.1.2 写入

N个客户端同时写入N个不同的文件。每个客户端以一系列1MB的写入操作向新文件写入总计1GB的数据。总写入速率及其理论极限如图3(b)所示。由于我们需要将每个字节写入16个块服务器中的3个,每个输入连接的速度为12.5mb/s,所以总速率限制为67mb/s

一台客户机的写入速率为6.3MB/s,约为限制的一半。罪魁祸首是我们的网络协议栈,它与我们用于将数据推送到块复制副本的管道的交互性不是很好。将数据从一个副本传播到另一个副本的延迟会降低总体写入速率

16个客户端的总写入速率达到35mb/s(即每个客户端2.2mb/s),大约是理论限制的一半。与读取情况一样,随着客户机数量的增加,多个客户机并发地向同一块服务器写入的可能性更大。此外,16个写入端比16个读取端更容易发生冲突,因为每一次写入都会涉及三个不同的副本

写入速度得比我们想象中的要慢。实际上,这并不是一个大问题,因为尽管它增加了单个客户机可见的延迟,但在有大量客户机时对系统总写入带宽并没有显著影响

6.1.3 系统追加

图3(c)显示了记录追加的性能。N个客户端同时在单个文件后进行追加。追加操作的性能受存储文件最后一个块的块服务器的网络带宽限制,与客户端数量无关。一个客户端的传输速率为6.0MB/s,16个客户端的传输速率为4.8 MB/s,这主要是由于不同客户端的拥塞和网络传输速率的差异导致的

我们的应用程序倾向于同时生成多个这样的文件。换句话说,N个客户端同时追加数据到M个共享的文件,其中N和M在几十到数百之间。因此,在我们的实验中,块服务器的网络拥塞在实践中并不是一个重要的问题,因为当另一个文件的块服务器忙时,客户端可以去写另一个文件

图3:总吞吐量。顶部曲线显示了我们的网络拓扑所施加的理论限制。底部曲线显示实际测量的吞吐量。图中显示了95%置信区间的误差条,由于某些情况下的测量值方差较低可能难以辨认

6.2 真实场景中的集群

我们现在研究Google中使用的两个集群,它们具有一定的代表性。集群A经常被100多名工程师用于开发研究。一个标准的任务被用户手动初始化后连续运行时间数个小时,通常读取几MB到几TB的数据,进行转换或分析后,将结果写回集群。集群B主要用于处理生产数据。这些任务持续的时间要长得多,而且只需要偶尔的人工干预就可以连续地生成和处理多个数TB的数据集。在这两种情况下,一个“任务”由多台机器上的多个进程组成,他们同时读取和写入多个文件

集群 A B
块服务器个数 342 227
可用磁盘空间
已用磁盘空间
72 TB
55 TB
180 TB
155 TB
文件数
死文件数
块数量
735 k
22 k
992 k
737 k
232 k
1550 k
块服务器元数据大小
Master服务器元数据大小
13 GB
48 MB
21 GB
60 MB

表2 两个GFS集群的特征值

6.2.1 存储

如表2中前五行所示,两个集群都有数百个块服务器,支持数TB的磁盘空间,但并没有完全占满。“已用空间”中包含了所有区块副本。几乎所有文件都复制了三次,因此,集群实际上各存储了18TB和52TB的文件数据

这两个集群的文件数量相近,尽管B有更多的死文件,即被删除或替换为新版本但存储空间尚未回收的文件。由于集群B的文件往往更大,所以块数量相对也更多

6.2.2 元数据

块服务器聚合存储了数十GB的元数据,主要是来自用户数据的64KB块的校验和。块服务器中保存的唯一其他元数据类型是在第4.5节中讨论的区块版本号

保存在master服务器上的元数据要小得多,只有几十兆字节,每个文件平均大约100字节。这与我们的假设一致,即master服务器内存大小实际上并不是系统总容量的瓶颈。大多数文件元数据是前缀压缩格式存储的文件名。其他元数据包括文件所有权和权限、从文件到块的映射以及每个块的当前版本号。此外,对于每个块,我们存储当前副本位置和用于实现写时拷贝的引用计数

每个服务器,包括块服务器和master服务器,只有50到100mb的元数据。因此恢复速度很快:在服务器就响应查询请求前,只需花费几秒钟从磁盘读取此元数据。然而,在master服务器从所有块服务器中寻找到块位置信息之前,它在一段时间内(通常是30到60秒)有点“步履蹒跚”

6.2.3 读写速率

表3显示了不同时间段的读写速率。在进行这些测量时,这两个集群都已经运行了大约一周的实践(最近这两个集群因为GFS系统升级进行了重启)

集群重新启动之后,平均写入速率小于30MB/s。当我们进行这些数据测量时,集群B正进行着大约100mb/s数据的写入操作,因为写入操作被传播到三个副本块,因此产生了一个300mb/s的网络负载

读取速率远远高于写入速率。正如我们所设想的那样,总工作负载由比写操作更多的读操作构成。两个集群都处于繁重的读操作中。尤其是,集群A在前一周一直保持着580mb/s的读取速率。它的网络配置可以支持750mb/s的速度,因此它能够高效地使用资源。群集B可以支持1300MB/s的峰值读取速率,但其应用程序仅使用了380 MB/s

集群 A B
读取速率(前一分钟)
读取速率(前一小时)
读取速率(重启以来)
583 MB/s
562 MB/s
589 MB/s
380 MB/s
384 MB/s
49 MB/s
写入速率(前一分钟)
写入速率(前一小时)
写入速率(重启以来)
1 MB/s
2 MB/s
25 MB/s
101 MB/s
117 MB/s
13 MB/s
master节点ops[1](前一分钟)
master节点ops(前一小时)
master节点ops(重启以来)
325 Ops/s
381 Ops/s
202 Ops/s
533 Ops/s
518 Ops/s
347 Ops/s

表3:两个GFS集群的性能测试

6.2.4 Master服务器负载

表3还显示,发送到主机的操作速率约为每秒200到500个操作。Master服务器可以很容易地跟上这个速度,因此不是这些工作负载的瓶颈

在GFS早期版本中,master节点有时是一些工作负载的瓶颈。它大部分时间都在大目录(包含数十万个文件)中按顺序扫描,寻找特定的文件。之后我们更改了master服务器的数据结构,以允许在命名空间中进行高效的二进制搜索。它现在可以轻松地支持每秒数千次的文件访问。如果有必要,我们可以通过在命名空间数据结构前面设置名称查找缓存来进一步提高查询速度

6.2.5 恢复时间

当某个块服务器失效之后,某些块的副本数量会不够[2],必须克隆才能恢复到原有复制级别。恢复这些块所需的时间取决于资源量。在一次实验中,我们关闭了集群B中的一个块服务器中的15000个数据块,总共包含600GB的数据。为了减少对正在运行的应用程序的影响,并为系统调度决策提供修正空间,我们的默认参数将此集群的并发克隆数限制为91个(占块服务器总数的40%),其中每个克隆操作最多允许消耗6.25MB/s(50 Mbps)。最终系统以440mb/s的有效复制速率,在23.2分钟内恢复了所有块

在另一次实验中,我们关闭了两台块服务器,每台服务器都有大约16000个块和660gb的数据。这两个节点的下线导致266个块只剩下一个副本。这266个数据块会以更高的优先级克隆,并在2分钟内全部副本个数恢复到至少2倍,从而使集群处于可以容忍一台块服务器故障而不会丢失数据的状态

6.3 工作负载分析

在本节中,我们将详细介绍两个GFS集群上的工作负载,这两个集群与第6.2节中的集群类似,但并不完全相同。X集群用于研发,Y集群用于生产数据处理

6.3.1 方法论和注意事项

这些结果中仅包含客户端发起的请求,因此它们反映了我们的应用程序对整个文件系统产生的工作负载。它们不包括执行的客户机请求,或内部后台活动的服务器间请求,比如向前转发的写入操作或重新平衡操作

我们基于从GFS服务器记录的实际RPC请求,启发式地重构出有关I/O操作的统计信息。例如,GFS客户机代码可能会将一个读操作分解为多个rpc请求以提高并行性,我们从中对原始读操作进行推断。因为我们的访问模式是高度程式化的,所以我们预计任何错误都会出现在噪音误差中。应用程序的显式日志记录可能会提供更精确一点的数据,但在逻辑上不可能重新编译并重新启动数千个正在运行的客户端来执行此操作,而且从那么台机器上收集结果也很麻烦

大家应该避免从我们的工作负载数据中过度归纳结论。由于Google完全控制GFS及使用GFS的应用程序,因此应用程序会针对GFS进行优化,另一方面,GFS也是为这些应用程序设计的。在一般应用程序和文件系统之间也存在这样的相互关系,但是在我们的例子中这样的效应的可能会更明显

6.3.2 块服务器工作负载

表4显示了按涉及数据量大小划分的操作分布情况。读取的大小情况呈现双峰分布。小读取操作(64KB以下)来自搜索密集型客户机,它们在大文件中查找小块数据。较大的读取操作(超过512kb)来自整个文件的长顺序读取

在集群Y中,大量的读取操作根本不返回任何数据。我们的应用程序,特别是生产系统中的应用程序,经常使用文件作为生产-消费队列。生产者并行地在文件后追加数据,而消费者从文件尾部读取数据。当消费者的读取速率超过生产者的写入速率时,将不会返回任何数据。集群X很少显示这种情况,因为它通常用于短期的数据分析任务,而不是长期的分布式应用程序

写入的大小情况也呈现出双峰分布。较大的写操作(超过256KB)通常是由于写入程序内部显著的缓存机制造成的。写操作会缓存较小的数据、频繁地设置检查点或执行同步操作,或简单地统计较小的写入操作(小于64 KB)再生成一次批量写入操作

关于记录追加操作,集群Y中看到的大型记录追加的百分比远高于集群X,因为我们的生产系统使用了Y集群,所以针对GFS进行了积极的优化

表5显示了按操作涉及的数据量大小统计的传输数据总量。对于所有类型的操作,较大的操作(超过256kb)通常占传输字节的大部分。由于随机搜索带来的工作负载,较小的读取(64KB以下)虽然传输数据量很小,但占据了相当重要的部分

表4 按数据大小细分的操作分布(单位:%)。对于读取,数据大小是实际读取和传输的数据量,而不是请求的数据量

表5:按操作涉及数据大小细分的传输字节数(单位“%)。对于读取,大小是实际读取和传输的数据量,而不是请求的数据量。如果读操作试图超过文件尾,两者可能会有所不同,这在我们的工作负载是很常见的

6.3.2 记录追加与写入

记录追加操作被我们大量地使用,特别是在生产系统中。对于集群X,写入与记录追加的比率是108:1(传输的字节数比)和8:1(操作计数比)。对于生产系统使用的集群Y,这两个比率分别为3.7:1和2.5:1。此外,这些比率表明,对于这两个集群,记录追加操作的占比往往大于写入操作。但是,对于集群X,在测量期间,记录追加的占比相当低,因此结果可能会被一个或两个具有特定大小的缓存的应用程序干扰

正如预期的那样,我们的数据修改操作的工作负载主要是由于记录附加而不是覆盖。我们测量了主副本上覆盖的数据量,结果近似于客户端故意覆盖之前的记录而不是追加新数据的情况。对于集群X,覆盖写操作占总修改字节数的0.0001%以下,占总修改操作数的0.0003%以下。对于集群Y,两者的比率均为0.05%。虽然这是某一小段时间的结果,但仍然比我们预期的要高。结果表明,由于错误或超时,这些覆盖操作大多来自客户端重试,它们本身不是工作负载的一部分,而是重试机制的结果

6.3.4 Master工作负载

表6显示了Master服务器上按请求类型的划分。大多数请求都是查询块位置信息(FindLocation)用于读取,以及查询租约持有者信息(FindLeaseLocker)用于数据修改

集群 X Y
打开文件(Open) 26.1 16.3
删除文件(Delete) 0.7 1.5
块位置查询(FindLocation) 64.3 65.8
租约持有者查询(FindLeaseLocker) 7.8 13.4
文件模式匹配查询(FindMatchingFiles) 0.6 2.2
其他组合操作(All other combined) 0.5 0.8

表6:按类型划分的master服务器请求(单位:%)

集群X和Y的删除请求数量差别很大,因为集群Y存储的生产数据集会定期重新生成并替换为较新版本。这些差异还有一部分隐藏在open请求的差异中,因为旧版本的文件可能会被覆写模式下的写操作(类似Unix中open函数的“w”模式)隐式删除

FindMatchingFiles属于模式匹配请求,支持“ls”和类似的文件系统操作。与master上其他请求不同,它会检索命名空间中的大部分内容,因此需要花费昂贵资源。集群Y使用的更为频繁,因为自动化数据处理任务倾向于检查文件系统的各个部分,以了解全局应用程序状态。相比之下,集群X的应用程序在更明确的用户控制下,通常会提前知道所有需要的文件的名称

7. 一些经验之谈

在构建和部署GFS的过程中,我们遇到了各种各样的问题,有些是操作性的,有些是技术性的

最初,GFS被认为是我们生产系统的后端文件系统。随着时间的推移,在使用上逐渐增加了研究和开发任务的支持。它最初很少支持权限和配额之类的内容,但现在初步包含了这些内容。虽然生产系统受到良好的约束和控制,但使用方却往往没有,所以需要更多的基础设施来防止用户间的相互干扰

我们最大的问题是与磁盘和Linux相关的。我们的许多磁盘都声明支持某一系列IDE协议版本的Linux驱动程序,但实际上只对较新的版本能做出可靠的响应。由于协议版本非常接近,这些驱动器大多可以正常工作,但偶尔发生不匹配会导致驱动器和内核对驱动器的状态不一致。这些内核中的问题会悄悄地损坏数据。这个问题促使我们使用校验和来检测数据损坏,同时我们修改了内核来解决这些协议不匹配的问题

早些时候,我们在使用Linux2.2内核时遇到了一些问题,主要是由于fsync()的开销。其效率与文件的大小成正比,而不是修改部分的大小。这对于我们的大型操作日志是一个问题,特别是在我们尚未实现检查点机制之前。我们通过使用同步写入在一段时间内解决了问题,并最终迁移到了Linux2.4

另一个关于Linux的问题是单读写锁,地址空间中的任何线程在从磁盘进行分页时都必须持有这个锁(读锁),或者在mmap()调用中修改地址空间(写锁)。我们在系统中看到了轻负载下的短暂超时,并努力寻找资源瓶颈或零星的硬件故障。最终我们发现在磁盘线程正在对以前映射的数据进行分页时,这个锁阻止了主网络线程将新数据映射到内存中。由于我们主要受到网络接口的限制,而不是内存拷贝带宽的限制,因此我们通过用pread()替换mmap()来解决这个问题,代价是额外的拷贝

尽管偶尔会出现一些问题,但Linux代码的可用性已经帮助我们一次又一次地探索和理解系统行为。在适当的时候,我们会改进内核并分享到开源社区

8. 相关工作

与其他大型分布式文件系统(如AFS[5])一样,GFS提供了一个与位置无关的命名空间,使数据能够透明地迁移,以实现负载平衡或容错。与AFS不同,GFS以更类似于xFS[1]和Swift[3]的方式,把文件分布在不同的存储服务器上,以提高整体性能和容错能力

由于磁盘相对便宜,并且采用复制的方式比更复杂的RAID[9]方案简单,GFS目前只使用复制来实现冗余,因此比xFS或Swift消耗更多的原始存储空间

与AFS、xFS、Frangipani[12]和Intermezzo[6]等系统不同,GFS不在文件系统接口下面提供任何缓存。我们的目标工作负载在单个应用程序运行中几乎不会被重用,因为它们要么通过一个大的数据集进行流式处理,要么在其中进行随机搜索,每次读取少量数据

一些分布式文件系统,如fragipani、xFS、Minnesota的GFS[11]和GPFS[10]删除了集中式服务器,并依赖分布式算法来实现一致性和管理。为了简化设计,提高可靠性和灵活性,我们选择集中式管理的方法。值得一提的是,一个集中的主节点使得实现复杂的块放置和复制策略变得更加容易,因为主节点已经拥有了大部分相关信息并控制着所有状态变更。我们通过减少主节点保存状态信息的数量,并在其他机器上进行完全复制来解决容错问题。可伸缩性和高可用性(用于读取)目前由我们的影子主机的机制提供。master节点状态的更新通过追加预写日志从而实现持久化,因此,我们可以采用Harp[7]中的主拷贝方案,以提供比当前方案更强的一致性保证和高可用性

我们正在解决一个与Lustre[8]类似的问题,即如何向大量客户端提供整体性能的保证。然而,我们专注于应用程序的需求,而不是构建一个兼容POSIX的文件系统,这极大地简化了问题。此外,GFS在设计时假定系统中存在大量不可靠的组件,因此容错性是我们设计的核心

GFS与NASD体系结构最为相似[4]。虽然NASD架构是基于网络连接的磁盘驱动器,但GFS使用商品机作为块服务器,就像NASD原型中所做的那样。与NASD的工作不同,我们的块服务器使用惰性分配的固定大小的块,而不是可变长度的对象。此外,GFS还实现了生产环境中所需的重新平衡、复制和恢复等功能

不同于Minnesota’s GFS和NASD,我们并不试图改变存储设备的模型。我们专注于解决复杂的分布式系统与现有商品组件的日常数据处理需求

基于原子性的记录追加操作的生产者-消费者队列解决了与River[2]中的分布式队列类似的问题。River使用了跨主机的基于内存的队列以及精密的数据流量控制,而GFS使用了一个持久化的文件,可以被多个生产者同时进行追加写入。River模型支持m-to-n分布式队列,但缺乏持久存储所带来的容错能力,而GFS只有效地支持m-to-1队列。多个消费者可以读取相同的文件,但它们必须进行协调划分传入的负载

9. 总结

Google文件系统展示了使用商品机的支持大规模数据处理工作负载的系统所必需的特点。虽然有些设计决策是针对我们特殊定制的,但许多决策可以适用于类似规模和成本的的数据处理任务

我们首先根据当前和预期的应用程序工作负载和技术环境重新评估传统的文件系统。我们的观察结果导致了设计空间中截然不同的思路。我们将组件故障视为常态而非例外,对主要采用追加写入(可能并发执行)然后读取(通常按顺序)的大型文件进行优化,同时扩展和放宽标准文件系统接口,以改进整个系统

我们的系统通过持续监控、复制关键数据和快速自动恢复来提供容错能力。区块复制机制允许我们能够允许部分块服务器的故障。这些故障的频繁发生激发了一种新的在线修复机制,该机制能够定期、透明地修复损坏并尽快弥补丢失的副本。此外,我们使用校验和来检测磁盘或IDE子系统级别的数据损坏,这种情况在系统磁盘数量较多时非常常见

我们的设计保证了在执行大量并发读写任务时能够提供高吞吐量。我们通过将控制流与数据流分离来实现这一点,其中控制流由Master服务器处理,数据流则是存在于客户端与块服务器之间。通过更大的数据块大小,以及将权限移交给数据修改操作中的主副本的数据块租约机制,可以最大限度地减少Master服务器的参与度。这使得一个简单的、集中的主机成为可能,而不成为瓶颈。我们相信,网络协议栈的改进将会解除当前单个客户端对写吞吐量的限制

GFS已经成功地满足了我们的存储需求,并在Google中广泛用作研发和生产数据处理的存储平台。它是一个重要的工具,使我们能够继续创新和攻关整个web系统中的问题

致谢

我们谨感谢下列人士对该系统或该文件所作的贡献。Brain Bershad(我们的领导)和匿名评论员给了我们宝贵的意见和建议。Anurag Acharya、Jeffedian和David desJardins为早期设计做出了贡献。Fay Chang致力于比较块服务器上的副本。Guy Edjlali负责存储配额。Markus Gutschke致力于测试框架和安全增强。David Kramer致力于性能优化。Fay Chang,Urs Hoelzle,Max Ibel,Sharon Perl,Rob Pike和 Debby Wallach评论了论文的早期草稿。谷歌的许多同事信任我们,敢于将他们的数据放在一个新的文件系统中,并给了我们有用的反馈。Yoshka帮助进行早期测试

参考文献

[1] Thomas Anderson, Michael Dahlin, Jeanna Neefe, David Patterson, Drew Roselli, and Randolph Wang. Serverless network file systems. In Proceedings of the 15th ACM Symposium on Operating System Principles, pages 109–126, Copper Mountain Resort, Colorado, December 1995.

[2] Remzi H. Arpaci-Dusseau, Eric Anderson, Noah Treuhaft, David E. Culler, Joseph M. Hellerstein, David Patterson, and Kathy Yelick. Cluster I/O with River: Making the fast case common. In Proceedings of the Sixth Workshop on Input/Output in Parallel and Distributed Systems (IOPADS ’99), pages 10–22, Atlanta, Georgia, May 1999.

[3] Luis-Felipe Cabrera and Darrell D. E. Long. Swift: Using distributed disk striping to provide high I/O data rates. Computer Systems, 4(4):405–436, 1991.

[4] Garth A. Gibson, David F. Nagle, Khalil Amiri, Jeff Butler, Fay W. Chang, Howard Gobioff, Charles Hardin, Erik Riedel, David Rochberg, and Jim Zelenka. A cost-effective, high-bandwidth storage architecture. In Proceedings of the 8th Architectural Support for Programming Languages and Operating Systems, pages 92–103, San Jose, California, October 1998.

[5] John Howard, Michael Kazar, Sherri Menees, David Nichols, Mahadev Satyanarayanan, Robert Sidebotham, and Michael West. Scale and performance in a distributed file system. ACM Transactions on Computer Systems, 6(1):51–81, February 1988.

[6] InterMezzo. www.inter-mezzo.org, 2003.

[7] Barbara Liskov, Sanjay Ghemawat, Robert Gruber, Paul Johnson, Liuba Shrira, and Michael Williams. Replication in the Harp file system. In 13th Symposium on Operating System Principles, pages 226–238, Pacific Grove, CA, October 1991.

[8] Lustre. www.lustreorg, 2003.

[9] David A. Patterson, Garth A. Gibson, and Randy H. Katz. A case for redundant arrays of inexpensive disks (RAID). In Proceedings of the 1988 ACM SIGMOD International Conference on Management of Data, pages 109–116, Chicago, Illinois, September 1988.

[10] Frank Schmuck and Roger Haskin. GPFS: A shared-disk file system for large computing clusters. In Proceedings of the First USENIX Conference on File and Storage Technologies, pages 231–244, Monterey, California, January 2002.

[11] Steven R. Soltis, Thomas M. Ruwart, and Matthew T. O’Keefe. The Gobal File System. In Proceedings of the Fifth NASA Goddard Space Flight Center Conference on Mass Storage Systems and Technologies, College Park, Maryland, September 1996.

[12] Chandramohan A. Thekkath, Timothy Mann, and Edward K. Lee. Frangipani: A scalable distributed file system. In Proceedings of the 16th ACM Symposium on Operating System Principles, pages 224–237, Saint-Malo, France, October 1997.


  1. ops即“operation per second”,每秒操作数 ↩︎

  2. 因为设置了负载因子,所以每个块都有最小副本数的限制 ↩︎