同样掉 4 块盘,为什么有的集群照常写、有的只剩只读 两套硬件几乎一样的 16 盘 S3 兼容集群同一周各掉了 4 块盘。一套业务毫无感知监控里只多了几条修复日志另一套上传接口开始成片报错运维盯着总共才坏 4 块盘百思不得其解。差别不在坏盘总数在于这 4 块盘落进了哪几个纠删码集erasure set以及那套集群的校验分片数是怎么设的。容错最容易被误读的一点是它的计算单位从来不是集群而是单个 erasure set。对象只认一个 set容错也只在那个 set 里算分布式对象存储不会把一个对象的分片撒遍全集群。它先把池pool里的盘按固定大小切成若干个 set上限通常是 16 块盘一组再对bucket/prefix/object做确定性哈希算出这个对象——以及它的所有版本——永远归属哪一个 set。写入时对象在这个 set 内被拆成 K 个数据分片 M 个校验分片落到该 set 的 N K M 块盘上。由此推出三条反直觉的结论坏盘要按 set 分开数。64 盘集群分成 4 个 set坏 4 块若均匀分散在 4 个 set 里每个 set 只少 1 块毫发无伤若 4 块全挤在同一个 setEC:4 的配置刚好踩线。一个 set 挂掉其余 set 照常服务。集群整体看起来还活着但哈希到那个 set 的对象全读不出来。这是最难排查的一类故障不是全挂是随机一部分 key 报错。别的 set 救不了它。set 之间完全独立没有跨 set 的冗余。要找回那部分数据只能靠站点复制或桶复制的远端副本。所以我的集群能坏几块盘这个问题本身就问歪了。该问的是每个 set 能坏几块以及我的坏盘会不会挤在同一个 set 里。读 quorum 和写 quorum 不是同一个数规则不复杂读 quorum K数据分片数。只要 set 内还有任意 K 块盘在线缺的分片就能从校验片重建出来读请求照常返回。写 quorum 通常也等于 K但有个例外当 M 正好等于 set 大小的一半时16 盘 set 配 EC:8写 quorum 变成 K1。多出来的这一块是防脑裂的——网络分区把 set 切成对半时两边都凑不出 K1谁也别想写避免两侧各写各的造成版本分叉。还有一个常被忽略的行为降级 set 上的新写入会自动提升校验比例。set 里已经掉了 2 块盘新对象不会仍按 EC:4 写而是按更高的校验比例写让这批对象拿到和健康 set 一样的持久性。代价是这段时间空间放大更严重、编码 CPU 开销更高。这是保命机制不是能长期依赖的状态——盘还是得换。谈能扛几块之前先把自己的实际配置查出来# 集群拓扑几个池、每池几个 set、set 多大、parity 多少mcadmin info cluster# 每台机器真实在线的盘数不是配置文件里写的那个mcadmin server info cluster--json|jq.servers[] | {endpoint, online: (.drives | length)}# 干跑一次修复看有多少对象处于降级状态mcadmin heal cluster--recursive--dry-run|head-20RustFS 侧对应的是rcrc cluster status# set 级健康度与掉线盘rc admin heal status# 当前修复进度分片摆在哪决定你掉一台机器等于掉几块盘这是假冗余最常见的来源。EC:4 名义上能扛 4 块盘但如果某个 set 的 16 个分片里有 4 个落在同一台服务器上那台机器一重启这个 set 立刻贴着阈值走钢丝落 5 个直接失去读 quorum。成熟实现的做法是跨节点条带化给每个 set 在每个节点上只挑 1 块盘节点数不够就绕圈补。16 节点 × 8 盘的部署会算出 8 个 16 盘 set每个 set 在每台机器上正好占 1 块——掉一整台机器每个 set 只少 1 块盘quorum 稳稳的。这条对自建集群尤其要紧因为拓扑是在初始化那一刻定死的事后改不了。上线前确认两件事节点数最好不小于 set 大小别出现某个 set 在一台机器上占了 3 块盘的情况故障域不只是机器。同一个机柜、同一路市电、同一块背板或存储控制器都可能让分散在 4 台机器上的盘一起消失。一个粗暴但有效的自检把 set × 节点的分布画成一张表然后问自己——拔掉任意一台机器的电源这个 set 少几块盘拔掉整个机柜呢修复不是免费的盘换上去只是开始数据得重建回来。修复一般有三层触发成本天差地别读时在线修复每次 GET/HEAD 校验分片发现缺损当场从校验片重建并回写。开销可忽略、客户端无感知但只覆盖被读到的对象。后台扫描器周期性巡检每个周期只扫存储池很小一部分RustFS 文档里给的量级是约 1/1024做轻量校验主要防静默的比特腐化。覆盖全量但很慢。手动全量修复一条命令把整个集群扫一遍重建。这是换盘后唯一能快速把冗余度拉回来的手段也是唯一会明显吃掉业务带宽和磁盘 IO 的手段。# MinIO只修受影响的桶比全集群 heal 温和得多mcadmin heal--recursivealias/photos# RustFS只修刚换上的那块盘并实时盯进度rc heal--disk/mnt/disk1 rc heal status--follow修复期间有两个绕不开的现实网络先扛不住。重建一块 16 TB 的盘意味着要从同 set 的其他盘读走十几 TB 数据、重算、再写回。万兆是这类集群的舒适下限节点间带宽不够时修复会把业务的 P99 拖到平时的几倍。加速开关要挑时间。强制全速修复能把恢复窗口从几天压到几小时代价是这段时间集群基本只干这一件事。更稳的做法是排在业务低谷跑并给修复停滞单独设告警——修复卡住不动比修复慢危险得多。换盘本身还有个低级但高频的坑挂载点必须按 LABEL 或 UUID 写进/etc/fstab别靠/dev/sdX。换盘重启后盘符顺序会变服务起来认错盘轻则修不动重则把好盘当空盘格式化。umount/dev/sdb mkfs.ext4 /dev/sdb-LDISK1# 沿用原来的卷标blkid /dev/sdb# 想用 UUID 就取这个值改 fstabmount-adf-h|grep/mnt/disk systemctl start rustfs三件初始化之后改不了的事set 的大小和数量池初始化时就算死了之后不能改。想换布局只能加新池再迁数据。调高 parity 不会重写老对象新参数只对之后写入的对象生效历史数据仍按老比例躺着。想让老数据也提升冗余得整体重写一遍比如 mirror 到新桶再切流量。扩容加的是新池老数据不会自动搬新池承接新写入老池继续背着老数据。容量看着均衡了热点还压在老池上。要真正摊平得显式做 rebalance而 rebalance 本身又是一次全量数据搬运。多池部署还有条硬约束值得记死任何一个池的所有 set 都失效整个部署会停掉全部 I/O哪怕其他池完全健康。原因是路由算不出对象该去哪个池硬撑着服务只会制造版本分叉。所以池不是越多越安全——多一个池就多一个能拖垮全局的单点。自建选型时值得多问一句不管选哪套自建方案POC 阶段有个问题该问清楚修复的粒度和可观测性。有的实现只能全集群 heal没法只修一块盘有的修复进度只有一行日志跑没跑完全靠猜。这两点在真出事的凌晨三点决定你是从容换盘还是干瞪眼。RustFS 在这块把三层修复读时在线、后台扫描、手动触发拆得比较明确rc heal --disk能指定单盘修rc heal status --follow能实时看进度set 级的降级状态在rc cluster status里直接暴露。仓库在 https://github.com/rustfs/rustfs 。它还年轻超大规模多池 rebalance 这类场景的公开实战案例不算多所以做重要集群选型时自己搭一套、拔几块盘试试比看任何文档都实在。一份能在半天内跑完的自检清单跑mc admin info cluster或rc cluster status记下几个池、几个 set、set 多大、parity 多少。用这几个数算出每个 set 的读/写 quorum以及最多能坏几块盘贴在运维文档第一页。画一张 set × 节点分布表检查有没有哪个 set 在某台机器或某个机柜上占了超过 M 块盘。在测试集群上真拔一块盘读还正常吗写还正常吗告警响了吗修复自动开始了吗记录一次完整修复要多久、期间业务 P99 涨多少——这个数字才是你真实的恢复时间目标。把/etc/fstab里所有/dev/sdX改成LABEL或UUID。前五步一个下午能做完第六步十分钟。真掉盘那天这半天省下的可能是一整个通宵。