1 分布式存储系统要理解什么
在单机上,进程调用文件接口,把字节交给文件系统,再由设备保存。文件写入成功以后,运维人员仍要面对磁盘损坏、主机断电、目录误删与空间耗尽。把数据放到多台机器,并不会让这些问题消失;它只是提供了把损失限制在局部的机会,同时增加了网络中断、节点状态不一致和后台搬运数据等新问题。
分布式存储系统的基本任务,是让客户端通过某种接口读写数据,同时把数据分布到多个存储节点。客户端可能是数据库进程、挂载目录后的 shell,也可能是通过 HTTP 上传文件的程序。节点之间依靠网络传输数据,控制信息则回答“数据应当在哪里”“哪些节点暂时不可达”“是否需要补足保护”。学习时应先分清客户端请求、数据位置和故障处理,避免把所有行为都归结为“集群在同步”。
本课使用机房文件归档场景:运维进程持续上传日志归档和磁盘镜像,另有分析进程读取旧文件。集群有多个存储节点,节点的磁盘挂载在专用目录,彼此通过交换网络通信。某台节点离线后,旧数据仍可能可读;但剩余副本减少,系统需要修复。扩容加入新节点时,系统还可能迁移部分健康数据,使容量和负载重新分布。这些后台工作都会消耗真实的磁盘、CPU 与网络资源。

判断系统是否符合需求,至少要分开看容量、性能、可用性与耐久性。容量回答能保存多少有效数据;性能回答请求在多大并发下需要多久;可用性回答需要服务时能否完成请求;耐久性回答经过设备故障等事件后,已确认保存的数据是否仍存在。读请求超时不等于数据丢失,读请求恢复也不能证明此前丢失的数据已经回来。必须把“现在能访问”与“长期不丢”分别设计和验证。
另一个基础词是故障域。磁盘、主机、机架和供电区域都可能构成故障域,具体取决于哪些组件会同时失效。把 3 份数据放在同一主机的 3 块盘上,能应对部分单盘故障,却无法应对主机整体掉电。分布式存储的保护能力来自数据布局与故障假设共同成立,节点数量本身不是保证。后文每次讨论“能容忍几个故障”,都要先说明故障发生在哪个范围。
2 对象、块与文件接口
对象、块与文件,首先是客户端看到的接口区别。对象接口通常用对象键标识数据,通过上传、下载和删除等请求访问。对象可以带元数据,适合归档包、镜像文件和备份片段。客户端常把修改后的内容作为新对象上传,不能默认任意位置改写、目录重命名或追加操作都具有本地文件系统的语义。界面上出现类似目录的前缀,也不自动意味着真实目录。
块接口把远端存储呈现为按地址读写的块设备,操作系统可以在上面创建文件系统,数据库也可能通过相应设备使用它。客户端看到的是设备容量与块读写,文件名、目录和权限通常由其上层文件系统管理。某个卷能被多台机器连接,并不意味着普通本地文件系统就能安全地被多台主机同时写入;并发挂载是否受支持,要由卷与文件系统的设计共同决定。
文件接口让客户端使用路径、目录和文件操作,通常通过网络文件协议或挂载客户端接入。shell 可以执行 ls、stat、cp 等操作,应用也可以继续使用文件描述符。然而,本地调用外观相似,不代表故障时行为相同。网络文件系统可能存在属性缓存、挂载重试与服务端状态恢复;一次路径查询变慢,也可能是元数据请求阻塞,而不是文件内容所在磁盘读得慢。
| 接口 | 客户端主要标识 | 常见接入方式 | 需要先确认的语义 |
|---|---|---|---|
| 对象 | 对象键 | HTTP 请求或 SDK | 覆盖、列举与成功响应的含义 |
| 块 | 设备与块地址 | 远端卷映射 | 持久化边界与共享写入限制 |
| 文件 | 路径与文件描述符 | 网络文件系统挂载 | 缓存、锁与重命名行为 |
在 Linux 客户端上,findmnt -T /mnt/archive 可以帮助确认路径位于哪个挂载点,df -h /mnt/archive 展示文件系统报告的容量,lsblk -f 展示块设备与文件系统关系。对象桶通常不会直接出现在 lsblk 中;即使借助适配器挂载,也要检查适配器暴露了哪些语义。这些命令用来识别接入层,不能单独证明后端存有多少副本。
实际选择应从程序行为出发。归档进程生成完整压缩包后再上传,往往容易适配对象接口;已有程序依赖目录遍历、权限和文件锁,就需要认真评估文件接口;需要自己管理文件系统的主机可能采用块接口。接口相同的产品也可能具有不同的成功确认策略,因此还要查清:请求返回成功时,数据进入了哪些持久介质、哪些节点,以及客户端是否需要额外的刷新操作。
3 纠删码与多副本边界
多副本的直觉是把完整数据保存在多个位置。例如 3 副本布局保存 3 份完整内容,某个副本暂时不可用时,可以从其他健康副本读取。若忽略元数据、预留空间和其他开销,100 GiB 有效数据需要约 300 GiB 原始空间。完整副本通常便于读取和复制修复,但其实际读写延迟仍受确认策略、网络距离和磁盘状态影响,不能从副本数直接推出。
纠删码用数据分片与校验分片提供冗余。以示意性的 4+2 布局为例,把一组数据拆成 4 个数据分片,再计算 2 个校验分片。在选定编码与正确布局下,任意 4 个可用分片足以恢复原数据,所以最多缺失 2 个分片时仍可恢复。忽略额外开销,100 GiB 有效数据对应约 150 GiB 原始空间。这里的 2 指该编码组缺失的分片数,并不无条件等于可以损坏 2 个机架。
如果同一编码组的多个分片落在同一故障域,某次故障就可能同时带走多个分片。部署时必须说明每个编码组如何跨磁盘、主机或机架放置。也不能把“现在可恢复”当作没有风险:已经缺失 2 个分片的 4+2 编码组,保护余量已经耗尽;在修复完成前再失去必要分片,就可能无法重建。数据校验失败同样可能使分片不能作为有效恢复来源。

修复丢失的完整副本,通常需要从健康副本读取完整数据,再写入目标节点。修复纠删码分片,常需要读取多个健康分片,经过计算生成缺失分片。因此,纠删码节约的存储容量,可能换来更多恢复读取、更复杂的数据路径和额外计算。具体代价取决于编码、分片大小和实现;不能拿某个示例的流量系数当作所有系统的常数。
副本和纠删码还不能替代备份。客户端有权限删除数据时,删除可能很快传播到所有副本;错误内容也可能被完整地保护起来。独立备份、版本保留和恢复演练解决的是另一类问题。这里先建立边界:冗余主要服务于规定故障模型下的数据保存和访问,备份则需要额外考虑历史版本、管理权限隔离与恢复时间,不在本课展开其全部实现。
4 动手:对照一次修复与再平衡
实验目标是区分 2 种看起来都在“搬数据”的操作。修复是保护数量或分片完整性不足后,重新补足冗余;再平衡是节点成员、容量或布局目标变化后,重新分配数据。健康集群扩容后出现迁移,不必先判为数据损坏;故障节点恢复上线后仍在搬运,也可能是在补齐故障期间产生的差异。操作前应先读状态中的原因字段与任务类型。
准备隔离测试集群和可丢弃的数据集,不在生产节点模拟故障。先确认各节点独立保存数据,且测试管理端可以查看健康状态、成员、待修复字节和迁移任务。不同系统的管理命令没有统一标准,下面使用 storagectl 作为命令形状的占位符,实际执行时必须替换成所选测试系统的官方命令;它不是 Linux 自带工具,也不代表某个通用协议。
# 示意接口:替换为测试系统实际提供的只读管理命令
storagectl status
storagectl nodes list
storagectl tasks list
# Linux 观测命令;部分发行版需要安装相应工具
findmnt -T /srv/storage
df -h /srv/storage
ip -s link show dev eth0
ss -tin
iostat -xz 1 5
先记录健康基线:有效数据量、原始占用、各节点空闲容量、读写吞吐、请求延迟的 P95 与 P99、磁盘延迟以及网络收发速率。归档数据集应事先记录文件大小与校验摘要。文件接口可用 sha256sum sample.tar 对固定样本生成摘要,对象接口则应完整下载到临时路径后再校验。比较必须针对同一份静止内容,持续写入中的文件不能随意作为一致性样本。
第 1 轮在测试管理界面按文档使 1 个节点离线,或只停止该节点的测试存储进程。记录离线时间与系统判定状态,观察是否出现降级数据、修复队列和目标节点写入。不同系统可能等待一段时间才修复,以避免短暂重启造成大量搬运。等待期间数据可能仍可读,但保护余量已经减少;应把“服务未中断”和“冗余已恢复”的时刻分别记录。
第 2 轮先恢复健康基线,再加入 1 个具有空闲容量的测试节点。观察新节点接收数据、旧节点释放部分占用,以及迁移任务逐渐完成。此时应确认冗余是否始终满足目标,再判断这些流量属于再平衡。不要为了制造差异而手工删除数据目录里的分片:这会绕过系统管理流程,可能同时破坏内容与元数据,使实验结果失去解释基础。
把两轮结果写在同一张记录表中:触发事件、任务原因、开始时间、完成时间、读取来源、写入目标、前台 P99 以及最终校验结果。实验结束后恢复节点、确认任务收敛,并重复校验样本。若只看总网络流量,两轮可能非常相似;加入触发原因与保护状态,才能解释系统到底在恢复安全余量,还是在调整健康数据的位置。
5 容量效率与重建成本取舍
估算容量时,先确定有效数据量,再计算冗余开销,最后加入运行余量。设有效数据量为 D,3 副本的基础原始占用约为 3D;k+m 纠删码的基础原始占用约为 D×(k+m)/k。这只是容量模型,尚未包含元数据、小对象开销、版本、快照、临时文件与设备预留。df 展示的可用空间和集群可接受的新数据量也可能不同,应明确每个指标的统计口径。
以 60 TiB 有效数据为例,3 副本基础占用约 180 TiB,4+2 基础占用约 90 TiB。如果将上述基础占用限制为计划可用原始容量的 75%,对应计划容量约为 240 TiB 与 120 TiB。这个例子展示计算方法,并不是推荐所有集群采用相同水位。真正的余量还必须覆盖故障后重新放置数据的需求,且空闲空间要位于符合故障域约束的节点上。
重建时间不能只用“丢失数据量÷网卡标称带宽”计算。网卡共享、磁盘读取、目标写入、编码计算、任务并发和前台负载都可能成为瓶颈。若要重建 600 GiB 内容,实际可持续的目标写入速率为 100 MiB/s,忽略其他限制时至少需要 6144 s,约 102 min。若修复还需要读取多份源分片,源端总读取与跨网络流量可能明显大于目标写入量。
提高修复带宽一般能缩短降级窗口,但也可能拉高前台延迟;降低修复带宽可以保护当前服务,却延长冗余不足的时间。因此限速并不是越低越安全。合理策略要同时观察前台延迟与修复收敛速度,并预先定义可接受的降级时长。若某节点磁盘已经持续拥塞,单纯提高任务并发可能增加排队,让有效修复速度反而下降。
容量效率还与访问粒度有关。大量小文件、频繁覆盖、追加写入和大块顺序读取,对接口与底层布局的压力不同。不能仅凭“纠删码更省空间”就把所有数据迁入相同布局。入门阶段可以先把长期保存、读取较少的归档,与频繁改写、强调低延迟的数据分开评估;至于具体编码优化、写入放大与缓存算法,留待具备实际工作负载证据后深入。
6 常见坑:把可用性当耐久性
最常见的误判是“今天所有下载都成功,所以数据很耐久”。成功下载只能说明被访问的数据在当时可读,未访问部分仍可能处于冗余不足或校验异常状态。反过来,网络分区可能让客户端无法访问健康数据,此时可用性下降,却不能直接判定数据已经丢失。报告中应分别记录请求失败、保护不足、校验异常和确认不可恢复的数据量。
第 2 类误判是只写“容忍 2 台机器故障”,却不说明数据布局和事件顺序。2 台机器是否处于同一机架,故障是否发生在前次修复完成之前,故障期间是否还允许写入,都会改变结果。写入确认策略也可能因降级状态而变化。方案应说明系统在哪些状态下接受写入、拒绝写入或降低保护,并把返回成功的持久化含义写清楚。
第 3 类问题是把进程存活当作存储健康。systemctl status 显示服务运行,仍可能存在数据盘掉挂载、只读文件系统、权限错误或目标路径落到系统盘等情况。检查路径应从 findmnt 和容量开始,再看存储进程日志与设备错误。系统盘被误写满以后,日志和控制进程也可能异常,最终看起来像多个互不相关的服务同时失败。

第 4 类问题是把备份与冗余混为一谈。误删、勒索式改写、程序写错内容与管理员误操作,可能穿透所有在线副本。备份是否有效要靠实际恢复验证,还要确认恢复所需的凭据、索引和软件环境都能取得。只统计备份任务退出码,而从不尝试读回数据,无法证明故障时一定能恢复。
第 5 类问题是用平均值掩盖局部危险。集群平均空闲 25%,不代表每个故障域都有足够空间接收修复数据;平均延迟正常,也可能有少量请求因重试达到很高的 P99。应按节点、磁盘和故障域观察偏斜,并关注最忙设备与最满位置。局部水位或局部拥塞往往先限制修复,随后才反映到全局平均指标。
7 案例:修复风暴挤占前台带宽
某归档集群在夜间出现节点离线,系统开始补足冗余。与此同时,定时归档任务仍在上传,分析进程也在下载历史镜像。值班人员看到客户端请求超时,便把上传进程的重试次数调高。结果交换链路流量持续接近上限,部分磁盘队列增长,前台 P99 进一步恶化。这里的“风暴”指大量并发后台恢复工作争用资源,不一定意味着修复算法自身出错。
排查先固定时间窗口:节点离线时间、修复任务启动时间、P99 上升时间与客户端重试增加时间。客户端记录请求完成率、超时和有效吞吐;存储端记录待修复字节、修复读写速率、任务数量及前台延迟;主机侧记录磁盘与网络指标。把这些变化放到同一时间轴,才能判断修复负载是否先于服务恶化,以及重试是否形成额外放大。
ip -s link show dev eth0 提供累计收发与错误计数,比较固定间隔的差值才能得到速率。ss -tin 可以辅助观察 TCP 连接状态与重传等信息,但连接数量多不能单独证明带宽耗尽。iostat -xz 1 5 提供设备排队与延迟线索,具体字段应结合设备类型解释,不能把单个利用率百分比当作性能极限的充分证据。存储任务指标则负责说明这些读写来自什么工作。

处置顺序先保护证据和数据,再控制竞争。确认是否只有预期的故障节点,是否存在额外磁盘错误或容量不足;随后按系统支持的方式降低修复并发或带宽,并观察前台延迟是否恢复,同时确认待修复量仍在下降。可以延后非紧急再平衡和批量扫描,减少可推迟的负载。不要直接终止全部修复进程,也不要在保护不足时盲目重启更多节点。
客户端也要避免无限重试。超时后应根据接口语义判断操作是否可能已经完成,并设置有限次数与退避。上传归档可以使用可核对的对象键、长度和校验信息,帮助识别结果,不能把所有超时都当作“服务端没有写入”。若应用不断重传完整大文件,即使修复限速生效,前台流量仍可能压满链路。
最终记录应包含调整前后的前台 P99、有效吞吐、修复速率与预计完成时间。若延迟改善但待修复量长期不降,处置只是把风险推迟;若修复很快但前台持续不可用,也不符合服务目标。事后要把这次实际可用带宽、重试放大和磁盘瓶颈写入容量规划,并在故障演练中重复验证限速策略,而不是只记住一个并发数字。
8 把耐久性目标写进存储方案
可执行的耐久性目标应从需要保护的数据开始。明确有效数据规模、每日增长、保留期限、对象或文件大小分布,以及是否允许重建原始数据。原始日志、可重新生成的索引和唯一的实验结果,丢失后果并不相同。把它们放在不同保护等级中,比给所有数据写同一句“高可靠”更容易落实,也能让预算与风险形成清楚的对应关系。
然后写故障假设与布局约束。例如,关键归档采用 3 副本,副本跨 3 个独立主机故障域,并说明机架断电是否属于当前承诺范围。若采用 4+2,还要说明编码组分片的故障域分布。不要仅凭这些参数推算某个年度丢失概率;设备故障相关性、修复窗口、运维错误和实现缺陷都会影响结果,入门方案宜先给出能通过演练验证的故障条件。
接着定义写入成功、降级与恢复完成。写入成功需要满足哪些持久化条件;保护不足时能否继续写入;哪些状态必须告警;从故障发生到完成修复允许多久。可用性指标使用成功率、延迟与观察窗口,耐久性则关注已确认数据能否保留和恢复。两组指标可以关联,但不能互相替代,更不能只用服务进程启动成功作为验收。
运维部分要包含容量余量、带宽预算与责任人。设定节点和故障域水位,预留故障后可用的目标位置,给修复与再平衡配置受支持的限速,并规定前台压力变化时如何调整。告警至少覆盖保护不足、修复停滞、空间紧张、校验错误与前台持续超时。每项告警需要对应检查入口和升级条件,否则再完整的指标面板也难以指导处置。
最后安排恢复验证。抽样读回与摘要校验用于检查样本内容,故障演练用于检查保护与修复路径,独立备份恢复用于检查误删后的恢复能力。说明每项验证的覆盖范围,避免将样本成功写成全量数据已经验证。验收记录应保留测试负载、故障范围、恢复时间与观察结果,使下一次扩容或接口迁移能够复用同一套判断依据。
9 从分布式存储系统走向分布式数据库(S55)
到这里,我们讨论的是字节如何通过接口保存、如何跨节点保护,以及故障后如何重新获得冗余。应用真正需要的常常不只是一个归档文件,还需要按字段查找、修改多条记录,并在并发请求下维持业务约束。这就把问题从“数据块或对象能否保存”推进到“数据记录的读写应当满足什么规则”,下一课将进入分布式数据库。
底层存储的多个副本,并不会自动赋予应用事务能力。某个程序依次修改 2 个文件,中途进程崩溃,即使每个文件都有完整副本,也不代表这 2 次修改整体完成或整体撤销。同样,文件成功持久化,也没有自动回答另一客户端什么时候能读到最新内容。接口语义、数据库记录语义和应用约束,需要在各自层次上明确。
分布式数据库也会受到本课的资源约束。后台恢复挤占网络和磁盘时,查询与写入延迟可能上升;磁盘空间不足可能限制数据库继续写入;故障域布局会影响多个节点同时失效的概率。观察数据库时仍要保留进程、文件系统、设备与网络证据,不能看到数据库报超时就只调整查询参数。
进入 S55 前,可以用 3 个问题检查自己的解释是否完整:某次成功写入到底确认了什么;某个节点离线后数据为什么还可读;后台搬运究竟是在修复保护,还是在重新分配健康数据。能够沿着客户端、网络、节点与介质把答案串起来,就建立了继续讨论数据库复制、读写一致性与事务边界所需的基础。本课暂不展开共识协议、编码矩阵与数据库内部日志实现。
放进方案里
为日志归档与镜像仓库写方案时,先列出有效数据量、每日新增、保留期限、典型文件大小与并发读写,再说明哪些内容可以重新生成,哪些内容必须独立备份。接口选择要贴合现有进程:完整归档包可评估对象接口,依赖路径权限的程序需验证文件接口,需要自行管理文件系统的主机再评估块接口。给出上传成功、覆盖与删除的具体含义,避免仅写“支持存储”。
保护部分写出副本数或纠删码参数、故障域分布及允许的故障范围。容量预算先计算冗余占用,再加入元数据、版本和运行余量;空闲空间必须分布在可用于修复的位置。将误删恢复单独列出,明确备份保存位置、权限隔离、恢复入口和负责人员,不能把在线副本作为历史版本恢复的唯一依据。
性能部分同时给出正常与降级状态的目标,至少包含前台完成率、P99、有效吞吐和允许的修复时长。为修复与再平衡预留磁盘和网络预算,规定压力上升时如何限速、延后哪些后台任务,以及如何确认修复仍在收敛。客户端采用有界重试与退避,并通过对象键、长度或摘要核对超时请求的结果。
验收时执行隔离环境中的节点离线、节点加入和备份恢复演练,分别记录保护状态、任务原因、资源消耗、样本校验及恢复时间。交付物应包含状态查询入口、告警阈值、处置步骤与升级联系人。只有这些记录能够回答“哪里出故障、还能保护多久、怎样恢复”,耐久性目标才从描述变成可执行承诺。
10 小结:下一步分布式数据库(S55)
分布式存储通过接口、布局与恢复流程共同提供服务。选择接口时检查应用语义,选择冗余时检查故障域,评估容量时加入修复余量,处理故障时同时观察前台请求与后台收敛。可用性描述当前能否服务,耐久性关注已确认数据能否保留,独立备份则提供另一条恢复路径。
自测:扩容产生的迁移为何不一定是修复?3 副本为何不能替代备份?4+2 为何不能直接承诺容忍任意 2 个机架故障?前台延迟上升时,为何不能无限降低修复速率?尝试用本课实验中的状态、时间与资源证据回答,再进入 S55,讨论记录、查询与事务如何建立在这些存储能力之上。