Linux 基础体系 · 第 58/85 篇。示例面向现代主流 Linux 发行版;发行版差异、权限和生产风险会明确说明。

Linux 软件 RAID:mdadm、级别、重建、降级、监控和恢复

Linux 软件 RAID 由内核中的 md(multiple devices)驱动和用户空间工具 mdadm 共同实现。内核负责把多个块设备组合成一个逻辑块设备,例如 /dev/md0mdadm 负责创建阵列、写入 RAID 元数据、组装阵列、处理成员替换以及监控状态。

RAID 的目标主要是提高可用性、容量或吞吐,不是备份。RAID 无法防止误删除、文件系统损坏、勒索软件、主机整体损坏或同步写入的逻辑错误。

本文中的命令面向现代主流 Linux 发行版。设备名、配置文件路径、systemd 服务名和 initramfs 更新命令可能因发行版不同而变化;涉及真实磁盘的命令必须先确认设备身份。


1. RAID 在块设备栈中的位置

一个典型的 Linux 存储栈可以表示为:

物理磁盘
  └─ 分区,例如 /dev/sdb1
       └─ md RAID 成员
            └─ /dev/md0
                 └─ 文件系统,例如 ext4 或 XFS
                      └─ 挂载点,例如 /data

如果还使用 LVM,常见布局是:

物理磁盘
  └─ 分区
       └─ /dev/md0
            └─ LVM PV
                 └─ VG
                      └─ LV
                           └─ 文件系统

也可以把 RAID 放在 LVM 之上,但两者承担的职责不同:

  • RAID 提供成员冗余、条带化和重建能力。
  • LVM 提供逻辑卷划分、快照和容量管理。
  • 文件系统负责文件、目录、元数据和空间分配。
  • 挂载负责把文件系统接入目录树。

应用通常不应该直接访问 /dev/sdb1 这类 RAID 成员,而应该访问 /dev/md0、其上的逻辑卷或文件系统。

1.1 先确认设备,而不是凭设备名猜测

以下命令用于查看块设备拓扑:

lsblk -o NAME,PATH,SIZE,TYPE,FSTYPE,UUID,MOUNTPOINTS,MODEL,SERIAL
findmnt
cat /proc/mdstat

示例:

NAME    PATH      SIZE TYPE FSTYPE MOUNTPOINTS
sda     /dev/sda  1.8T disk
├─sda1  /dev/sda1 1.8T part
sdb     /dev/sdb  1.8T disk
└─sdb1  /dev/sdb1 1.8T part
md0     /dev/md0  1.8T raid1 ext4   /data

lsblk 显示的是当前设备树,/proc/mdstat 显示的是内核 md 阵列状态。两者都正常并不意味着底层磁盘健康,还需要检查 SMART:

sudo smartctl -a /dev/sdb

smartctl 属于 smartmontools,可能需要单独安装。对于硬件 RAID 控制器后的磁盘,SMART 查询方式还取决于控制器。


2. mdadm 管理的是什么

mdadm 不是文件系统工具,也不是通用的磁盘复制工具。它主要管理三类信息:

  1. 阵列配置:RAID 级别、成员数量、条带大小、布局。
  2. 成员状态:活动、备用、故障、移除、重建中。
  3. 超级块(superblock):阵列 UUID、设备角色、事件计数、阵列级别等元数据。

创建阵列时,mdadm 会把 RAID 元数据写入成员设备。之后系统可以根据超级块识别哪些设备属于同一阵列。

常用查看命令:

sudo mdadm --detail /dev/md0
sudo mdadm --examine /dev/sdb1
sudo mdadm --examine --scan
cat /proc/mdstat

其中:

  • --detail 查看已经组装的逻辑阵列。
  • --examine 直接读取成员设备上的 RAID 超级块。
  • --examine --scan 根据超级块生成可用于组装的阵列描述。
  • /proc/mdstat 是内核提供的实时概览,适合快速观察同步、重建和降级状态。

典型的 mdadm --detail 输出可能包含:

/dev/md0:
        Raid Level : raid1
        Array Size : 1953381376
     Used Dev Size : 1953381376
      Raid Devices : 2
     Total Devices : 2
           State : clean
  Active Devices : 2
 Working Devices : 2
  Failed Devices : 0
   Spare Devices : 0

这里的 Array Size 是阵列对上层提供的容量,不等于所有成员容量之和。Active Devices 是当前参与工作的成员,Working Devices 还包括可能尚未成为活动成员但设备仍可工作的成员;生产判断应结合完整输出,而不是只看一行。


3. RAID 级别的容量和容错条件

设:

  • NN 是成员设备数量;
  • SS 是可用于 RAID 的最小成员容量;
  • CC 是阵列可用容量。

实际容量还会受到超级块、分区对齐和元数据开销影响,下面的公式用于理解主要关系。

3.1 RAID0:条带化,没有冗余

RAID0 把连续数据分散到多个成员:

条带 0 → 磁盘 A
条带 1 → 磁盘 B
条带 2 → 磁盘 C
条带 3 → 磁盘 D

容量近似为:

C=N×SC = N \times S

但是任意一个成员失效,都可能导致整个阵列无法恢复,因为一个文件的数据可能分布在多个成员上。RAID0 的“性能”和“容量”不能被误解为可靠性。

创建示例:

sudo mdadm --create /dev/md0 \
  --level=0 \
  --raid-devices=2 \
  /dev/sdb1 /dev/sdc1

对于只要可用性而不是临时高速空间的生产数据,不应使用 RAID0。

3.2 RAID1:镜像

RAID1 把相同数据写入多个成员:

写入块 X
  ├─ 磁盘 A:X
  └─ 磁盘 B:X

等容量成员组成的 RAID1 容量近似为:

C=SC = S

两个成员时,可以容忍一个成员失效。三个成员时,理论上只要仍有一个一致且可工作的副本,阵列还可以继续提供数据;但多个成员同时故障后的安全性取决于故障顺序、事件计数和阵列状态。

创建两个成员的 RAID1:

sudo mdadm --create --verbose /dev/md0 \
  --metadata=1.2 \
  --level=1 \
  --raid-devices=2 \
  /dev/sdb1 /dev/sdc1

RAID1 适合系统盘、数据库镜像和需要简单恢复路径的场景。它不提供 RAID0 那样的容量叠加,写入也通常需要更新多个副本。

3.3 RAID5:条带化加单校验

RAID5 在每个条带中保存数据块和一个校验块。校验通常可以抽象为异或:

P=D0D1Dk1P = D_0 \oplus D_1 \oplus \cdots \oplus D_{k-1}

其中:

  • DiD_i 是同一条带中的数据块;
  • PP 是校验块;
  • \oplus 是按位异或。

如果丢失一个数据块,例如 D1D_1,可以计算:

D1=PD0D2Dk1D_1 = P \oplus D_0 \oplus D_2 \oplus \cdots \oplus D_{k-1}

因此,等容量成员组成的 RAID5 容量近似为:

C=(N1)×SC = (N-1) \times S

RAID5 只能容忍一个成员故障。两个成员同时失效时,单个校验无法唯一确定两个未知数据块。

例如四块 4 TB 磁盘组成 RAID5:

  • 原始容量:16 TB;
  • 近似可用容量:12 TB;
  • 可容忍:1 块成员故障;
  • 第二块成员在重建完成前故障:阵列通常无法继续恢复全部数据。

创建示例:

sudo mdadm --create --verbose /dev/md0 \
  --metadata=1.2 \
  --level=5 \
  --raid-devices=4 \
  /dev/sdb1 /dev/sdc1 /dev/sdd1 /dev/sde1

RAID5 的写入可能涉及“读-改-写”:

  1. 读取旧数据块;
  2. 读取旧校验块;
  3. 计算新校验;
  4. 写入新数据和新校验。

完整条带写入可以避免部分读操作,但随机小写仍可能产生较高的写放大和延迟。RAID5 还必须面对条带更新过程中断电造成的数据与校验不一致问题;具体保护能力取决于内核 md RAID5 实现、设备写入保证以及是否启用相应的保护机制,不能把“有校验”当成“断电事务一致性”。

3.4 RAID6:双校验

RAID6 保存两组独立校验,通常称为 P 和 Q。它不只是把 RAID5 的同一个异或校验复制一次,而是使用能够在两个未知块存在时求解的双校验算法。

等容量成员的容量近似为:

C=(N2)×SC = (N-2) \times S

RAID6 可以容忍任意两个成员同时故障。六块 4 TB 磁盘组成 RAID6 时,近似容量为 16 TB,可容忍两块成员故障。

RAID6 需要更多校验计算和写入工作,重建也可能更慢,但对于大容量磁盘和较长重建窗口,双故障容忍通常比 RAID5 更重要。

3.5 RAID10:镜像组上的条带化

RAID10 先形成镜像关系,再在镜像组之间条带化。四块磁盘的常见布局可以抽象为:

镜像组 0:A ↔ B
镜像组 1:C ↔ D

条带 0 → 镜像组 0
条带 1 → 镜像组 1
条带 2 → 镜像组 0
条带 3 → 镜像组 1

等容量成员的常见容量约为:

C=N2×SC = \frac{N}{2} \times S

RAID10 至少需要两个成员,但生产中通常使用四块或更多磁盘。它至少可以容忍一个成员故障;如果故障成员分布在不同镜像组,也可能同时容忍多个故障。如果同一镜像组的两个成员都失效,则对应数据丢失。

因此,“RAID10 可以坏两块盘”不是无条件结论。正确说法是:它可以容忍多个故障,但不能让同一镜像副本的两个成员都丢失。

创建四成员 RAID10:

sudo mdadm --create --verbose /dev/md0 \
  --metadata=1.2 \
  --level=10 \
  --raid-devices=4 \
  /dev/sdb1 /dev/sdc1 /dev/sdd1 /dev/sde1

mdadm 的 RAID10 还涉及 nearfaroffset 等布局参数。布局会影响副本位置、顺序访问和随机访问特性,不应仅凭“RAID10”三个字推断所有性能行为。若没有明确的性能测试和恢复要求,通常先使用发行版和 mdadm 默认布局,并记录实际配置。


4. 超级块、元数据和启动行为

mdadm 的超级块用于识别阵列成员。常见元数据版本包括:

  • 0.90:旧式格式,兼容性限制较多;
  • 1.0:超级块位于设备末尾;
  • 1.1:超级块位于设备起始位置附近;
  • 1.2:现代系统常见格式,超级块位于设备前部,但通常不会覆盖整个设备。

创建时可以显式指定:

--metadata=1.2

1.2 通常适合数据阵列,但如果阵列直接承载引导所需内容,必须同时考虑固件、GRUB、initramfs 和发行版支持。某些引导环境对超级块位置、组件启动方式或 RAID 级别存在限制。

创建阵列后,应把阵列识别信息写入系统配置。例如 Debian 系常见路径是:

sudo mdadm --detail --scan | sudo tee -a /etc/mdadm/mdadm.conf
sudo update-initramfs -u

其他发行版可能使用 /etc/mdadm.conf,并通过:

sudo dracut -f

更新 initramfs。配置文件路径和服务名称必须以目标发行版文档为准。若只创建了阵列却没有更新 initramfs,系统可能在启动早期无法组装根文件系统所在阵列。


5. 从创建到挂载:一个完整流程

以下示例假设 /dev/sdb1/dev/sdc1 已经是两个专用于 RAID1 的分区。它们不能包含需要保留的数据。

5.1 分区和设备准备

先确认设备:

lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS,MODEL,SERIAL
sudo wipefs -n /dev/sdb1
sudo wipefs -n /dev/sdc1

wipefs -n 只探测签名,不执行擦除。确认不再需要旧数据后,才可以使用不带 -nwipefs;对错误设备执行擦除会立即破坏可恢复性。

现代 GPT 分区通常应设置为 Linux RAID 分区类型。可以使用 fdiskgdiskparted 完成,但分区工具的语法和类型标识会因工具版本不同。重点是两个分区的起始位置对齐,并且成员容量足够一致。

5.2 创建阵列并等待初始同步

sudo mdadm --create --verbose /dev/md0 \
  --metadata=1.2 \
  --level=1 \
  --raid-devices=2 \
  /dev/sdb1 /dev/sdc1

创建后立即查看:

cat /proc/mdstat
sudo mdadm --detail /dev/md0

同步中的输出可能类似:

md0 : active raid1 sdc1[1] sdb1[0]
      976630336 blocks super 1.2 [2/2] [UU]
      [===>.................]  resync = 18.4% ...

[UU] 表示两个成员都在线;如果显示 [U_],表示第二个位置缺失。即使 RAID1 在同步过程中已经可以提供服务,也不代表它已经完成冗余建立。此时再次故障的风险更高。

5.3 在阵列上创建文件系统

不要在单个 RAID 成员上创建文件系统,而要在 /dev/md0 上创建:

sudo mkfs.ext4 /dev/md0
sudo mkdir -p /data
sudo mount /dev/md0 /data
df -h /data

文件系统 UUID 可以用于 /etc/fstab

sudo blkid /dev/md0

/etc/fstab 示例:

UUID=<md0-filesystem-uuid>  /data  ext4  defaults,nofail  0  2

nofail 是否适合生产环境取决于业务要求。它可以避免非根文件系统不可用时阻塞启动,但也可能让服务在数据目录没有挂载的情况下启动。对必须使用数据盘的服务,应让启动失败显式暴露问题,而不是静默使用空目录。


6. 阵列状态:clean、degraded、rebuilding 和 reshape

可以把 md 阵列的关键状态理解为数据可用性和冗余状态的组合。

6.1 clean

clean 表示阵列已同步完成,当前成员状态与阵列配置一致。它不等于:

  • 所有物理磁盘健康;
  • 文件系统没有错误;
  • 数据已经备份;
  • 没有潜伏坏块。

6.2 degraded:降级

降级表示阵列仍能提供数据,但缺少一个或多个预期成员。例如 RAID1 两个成员中掉了一个,状态可能是:

State : clean, degraded
Active Devices : 1
Working Devices : 1
Failed Devices : 1

对于 RAID5,缺一块仍可工作;对于 RAID6,缺两块仍可工作。降级期间阵列的容错余量降低,通常也会有额外的读、校验或重建压力。

降级本身不等于数据已经损坏,但它是一个必须处理的故障状态。

6.3 recovery:从备用或新成员恢复副本

recovery 通常表示从现有有效数据向备用成员或新加入成员复制缺失内容。例如 RAID1 更换故障盘后,活动成员把数据同步到新盘。

6.4 resync:重新同步

resync 通常表示对成员之间的内容进行同步或一致性检查。新建阵列后的初始同步也经常显示为 resync

6.5 reshape:改变阵列结构

reshape 是改变 RAID 结构,例如:

  • RAID5 增加成员;
  • RAID1 改变镜像成员数量;
  • 修改某些布局或条带参数。

reshape 不是普通故障重建。它可能移动大量已有数据,持续时间长,期间故障路径和恢复步骤也更复杂。执行前必须确认备份、供电和监控条件。


7. 故障成员的处理:fail、remove、add

假设 /dev/md0 是 RAID1,内核报告 /dev/sdb1 出错。

7.1 先区分瞬时错误和物理故障

先查看:

dmesg -T | grep -Ei 'ata|scsi|I/O error|uncorrect|reset|md'
sudo smartctl -a /dev/sdb
sudo mdadm --detail /dev/md0

如果只是线缆、背板或控制器瞬时异常,直接把盘标记为故障并移除可能扩大问题。反过来,如果磁盘已经出现大量不可校正错误,继续让它参与重建也可能导致更多 I/O 错误。

7.2 手动标记故障并移除

sudo mdadm --manage /dev/md0 --fail /dev/sdb1
sudo mdadm --manage /dev/md0 --remove /dev/sdb1

--fail 把成员标记为故障,使 md 不再把它作为正常副本使用;--remove 从当前阵列活动列表移除它。执行后检查:

cat /proc/mdstat
sudo mdadm --detail /dev/md0

如果磁盘只是暂时掉线,有时可以直接重新加入:

sudo mdadm --manage /dev/md0 --re-add /dev/sdb1

但只有在确认设备、分区和超级块仍然正确时才这样做。不要对一个已经确定存在介质故障的磁盘使用 --re-add 来掩盖问题。

7.3 更换磁盘并加入

新盘必须至少满足阵列要求的成员容量。实际可用容量由分区大小决定,因此“标称容量相同”不一定足够;不同型号的扇区数可能略有差异。

把新盘分区复制为与旧盘相同的布局时,必须明确源盘和目标盘。例如目标盘是全新的 /dev/sdb

sudo sfdisk -d /dev/sda | sudo sfdisk /dev/sdb

这条命令会覆盖目标盘的分区表,不能把 /dev/sda/dev/sdb 的身份搞反。复制后确认:

lsblk /dev/sdb
sudo fdisk -l /dev/sdb

然后加入阵列:

sudo mdadm --manage /dev/md0 --add /dev/sdb1

加入后,内核会开始 recovery。观察:

watch -n 2 cat /proc/mdstat

重建完成的典型状态:

md0 : active raid1 sdb1[2] sdc1[1]
      [2/2] [UU]

这里的成员编号不一定等于分区名中的顺序,因此应以 mdadm --detailStateActive Devices 和设备列表为准。


8. 重建的实际机制和速度取舍

以 RAID1 为例,重建过程大致如下:

  1. 阵列发现成员缺失,进入 degraded 状态。
  2. mdadm 监控或管理员把新成员加入阵列。
  3. 内核 md 驱动读取现有有效副本。
  4. 数据被写入新成员对应位置。
  5. 根据同步进度更新状态。
  6. 完成后阵列回到 clean、非降级状态。

RAID5/6 的重建更复杂。重建一个缺失成员时,系统需要读取其他成员并计算缺失数据;如果遇到不可读取扇区,可能导致重建失败或暴露潜在数据问题。

Linux md 可以使用 bitmap 记录哪些区域发生过写入。写意图位图(write-intent bitmap)使短暂掉线的成员重新加入时,只需同步可能变化的区域,而不是盲目扫描整个阵列。它不能替代完整校验,也不能保证长期离线成员只需同步极少数据。

查看是否启用 bitmap:

sudo mdadm --detail /dev/md0

某些阵列可以配置内部 bitmap:

sudo mdadm --grow /dev/md0 --bitmap=internal

该操作是否适用于已有阵列、对性能和元数据的影响,要结合 mdadm 版本和 RAID 级别确认。不要在不了解当前阵列状态时直接执行 --grow

内核通常通过以下参数限制 md 同步速度:

cat /proc/sys/dev/raid/speed_limit_min
cat /proc/sys/dev/raid/speed_limit_max

临时调整示例:

sudo sysctl -w dev.raid.speed_limit_min=50000
sudo sysctl -w dev.raid.speed_limit_max=200000

提高上限可能缩短重建时间,但会增加磁盘吞吐、队列深度和业务延迟;降低速度可以保护在线业务,却延长阵列处于低冗余状态的时间。调优不能脱离磁盘性能、业务 I/O 模式和故障窗口。


9. 监控:不能只看 /proc/mdstat

9.1 即时检查

cat /proc/mdstat
sudo mdadm --detail /dev/md0

/proc/mdstat 适合观察:

  • 是否有阵列;
  • 是否降级;
  • 是否正在 resync、recovery 或 reshape;
  • 大致进度和速度。

mdadm --detail 更适合确认:

  • RAID 级别;
  • 期望成员数;
  • 当前活动成员;
  • 故障成员;
  • 备用成员;
  • UUID 和事件信息。

9.2 mdadm monitor

常见的监控形式是:

sudo mdadm --monitor --scan --mail=ops@example.com

守护进程模式和邮件配置的具体参数、服务管理方式因发行版版本不同而变化。许多系统通过 systemd 的 mdmonitormdadm-monitor 或发行版自定义单元启动监控;应使用以下命令确认:

systemctl list-unit-files | grep -i md
systemctl status mdmonitor.service
systemctl status mdadm-monitor.service

不要假设服务名在所有发行版中相同。

监控至少应在以下事件发生时告警:

  • 阵列降级;
  • 成员故障或被移除;
  • 重建失败;
  • reshape 失败;
  • 阵列停止;
  • 监控服务自身停止。

告警系统应验证“邮件或事件是否真的送达”,不能只把命令写入配置文件就认为监控已经生效。

9.3 SMART 和系统 I/O 指标

RAID 状态正常并不代表磁盘没有潜在故障。应结合:

sudo smartctl -H /dev/sdb
sudo smartctl -a /dev/sdb
iostat -xz 5

iostat -xz 5 可以观察设备利用率、平均等待时间、队列长度和吞吐。重建期间,磁盘 %util 接近饱和、await 上升、队列变长,可能是正常的重建压力,也可能说明业务已经受到影响。要区分这两种情况,需要同时观察业务延迟和阵列进度。

RAID 监控回答的是“阵列是否缺成员”;SMART 和 I/O 监控回答的是“成员是否正在恶化”和“业务是否受到性能影响”。三者不能互相替代。


10. 在线扩容:RAID、分区、LVM 和文件系统是四个步骤

“给 RAID 增加磁盘后容量自动增加”是常见误解。在线扩容通常要经过以下层次:

新磁盘分区变大或增加新成员
  → mdadm 扩展阵列结构
    → md 设备容量变大
      → LVM PV/LV 扩展(如果使用 LVM)
        → 文件系统扩展

10.1 RAID1 增加成员

先把新分区加入:

sudo mdadm --manage /dev/md0 --add /dev/sdd1

这通常只会增加备用成员,不一定立即改变 RAID1 的有效容量。若要把 RAID1 从两个镜像成员扩展为三个:

sudo mdadm --grow /dev/md0 --raid-devices=3

不同 RAID 级别对 --grow 的支持和限制不同,执行前应检查当前 mdadm 版本的手册。扩展会触发同步,期间仍然存在额外 I/O 和故障风险。

10.2 RAID5 增加成员

典型顺序是:

sudo mdadm --manage /dev/md0 --add /dev/sde1
sudo mdadm --grow /dev/md0 --raid-devices=5

这会触发 reshape,将原有条带重新映射到新的成员数量。reshape 期间不能把阵列当作普通“加入一块备用盘”处理;必须监控进度,并在变更前确认备份和恢复方案。

10.3 RAID 设备之后的文件系统扩展

如果 /dev/md0 上直接是 ext4:

sudo mdadm --grow /dev/md0 --size=max
sudo resize2fs /dev/md0

如果是 XFS,扩展通常对挂载点执行:

sudo xfs_growfs /data

XFS 通常支持在线扩大,但不能按同样方式缩小。

如果 md 上是 LVM,则应先扩大 md 设备,再执行类似:

sudo pvresize /dev/md0
sudo lvextend -l +100%FREE /dev/vgdata/lvdata
sudo resize2fs /dev/vgdata/lvdata

对于 XFS:

sudo xfs_growfs /data

每一步都要验证:

sudo mdadm --detail /dev/md0
sudo pvs
sudo vgs
sudo lvs
df -h /data

缩容比扩容复杂得多,必须先缩小文件系统,再缩小逻辑卷、PV 和 RAID 结构。文件系统是否支持在线缩小也不同;错误顺序可能直接截断数据。


11. 阵列组装和启动恢复

正常启动时,系统根据 initramfs、mdadm.conf 和超级块自动组装阵列。手动检查成员:

sudo mdadm --examine /dev/sdb1 /dev/sdc1 /dev/sdd1

尝试扫描并组装:

sudo mdadm --assemble --scan --verbose

组装成功后:

cat /proc/mdstat
lsblk
sudo mdadm --detail /dev/md0

如果阵列没有自动组装,常见原因包括:

  • initramfs 没有包含 mdadm 配置;
  • 阵列配置文件缺失;
  • 成员设备尚未出现;
  • 磁盘连接或控制器有问题;
  • 超级块中的 UUID 不一致;
  • 阵列曾经异常停止,成员事件计数存在差异。

可以比较成员的元数据:

for d in /dev/sdb1 /dev/sdc1 /dev/sdd1; do
    echo "=== $d ==="
    sudo mdadm --examine "$d"
done

重点观察阵列 UUID、设备角色、事件计数和设备状态。不要仅凭设备文件名决定成员顺序。


12. 恢复时最危险的命令:--force--create--zero-superblock

12.1 --assemble --force

当成员的事件计数不一致时,--force 可能强行组装阵列。但这意味着管理员在接受一个判断:哪些成员包含较新的阵列状态,哪些成员可能是旧副本。

错误使用 --force 可能导致:

  • 旧数据覆盖新数据;
  • 阵列以错误成员组合启动;
  • 文件系统出现静默损坏;
  • 后续同步把错误传播到其他成员。

只有在已经保存各成员镜像或明确理解事件计数、设备角色和故障时间线时,才考虑使用它。恢复优先级应是先保护原始数据,再尝试组装。

12.2 不要用 --create “修复”已有阵列

对已有数据阵列执行:

mdadm --create /dev/md0 ...

可能重写超级块并破坏原始识别信息。即使使用了看似相同的 RAID 级别和成员数量,也不能保证条带大小、布局、设备角色和事件状态完全一致。

恢复已有阵列的第一选择通常是:

sudo mdadm --assemble --scan

而不是重新 --create

12.3 --zero-superblock 会删除 RAID 识别信息

以下命令会清除成员上的 md 超级块:

sudo mdadm --zero-superblock /dev/sdb1

它只适合已经确认不再属于任何需要保留的阵列成员、准备重新使用的设备。对仍有恢复价值的成员执行该命令,会增加恢复难度。


13. 不同故障路径的恢复策略

13.1 单成员故障,阵列仍在线

这是最常见的路径:

  1. 查看 mdadm --detail 和内核日志;
  2. 判断是暂时掉线还是物理故障;
  3. 物理故障时 --fail--remove
  4. 准备容量足够的新成员;
  5. --add 加入;
  6. 观察 recovery;
  7. recovery 完成后确认 [UU] 或对应完整状态;
  8. 检查 SMART 和硬件告警,找出故障根因。

只更换磁盘而不检查背板、电源、线缆和控制器,可能导致新盘再次掉线。

13.2 阵列降级但业务仍需运行

降级阵列通常可以继续挂载,但风险是容错能力已经下降。此时应减少不必要的高负载操作,尤其要谨慎处理 RAID5/6 的大规模校验和重建。

不要因为阵列还能读写,就把降级状态当成正常状态。恢复完成后仍应检查文件系统和业务数据,因为 RAID 只保证块级冗余,不保证应用事务逻辑。

13.3 多成员故障,超过 RAID 级别容错能力

RAID5 缺两块、RAID6 缺三块,通常无法从剩余信息唯一还原全部数据。此时不存在一个通用的 mdadm 参数可以“计算出”丢失的数据。

正确方向是:

  1. 立即停止可能造成写入的操作;
  2. 不要继续尝试随机组装组合;
  3. 尽可能对所有成员做扇区级镜像;
  4. 在镜像副本上进行恢复实验;
  5. 根据成员事件计数、设备角色和故障顺序重建阵列;
  6. 恢复文件后验证文件系统和业务数据;
  7. 必要时使用专业数据恢复工具或服务。

对有坏扇区的磁盘,优先考虑能够记录错误并断点续读的镜像工具,而不是反复对原盘执行高负载重建。

13.4 RAID1 成员内容不一致

RAID1 的两个成员都能读,并不表示它们的每个块都一致。若出现未被检测的静默损坏,系统需要依据阵列状态和同步策略决定使用哪个副本;管理员不能简单地假设“左边磁盘永远正确”。

因此:

  • 不要随意交换成员顺序;
  • 不要在没有备份的情况下强制指定同步方向;
  • 对关键数据执行文件级校验或应用级校验;
  • 结合事件计数、故障时间线和文件系统检查结果判断。

13.5 文件系统损坏不是 RAID 成员故障

如果阵列状态是 clean,但挂载失败:

sudo mdadm --detail /dev/md0
sudo fsck -f /dev/md0

fsck 是否安全取决于文件系统和挂载状态。通常文件系统检查应在卸载状态下进行;XFS 使用 xfs_repair,不能用 ext4 的 fsck 逻辑替代。

RAID 正常只说明 md 层能够提供块设备,不说明 ext4、XFS 或上层 LVM 元数据没有损坏。


14. 读取一致性检查和潜伏错误

长期运行的 RAID 可能存在“阵列看起来正常,但某些区域已经无法可靠读取”的问题。可以执行一致性检查:

echo check | sudo tee /sys/block/md0/md/sync_action
watch -n 2 cat /proc/mdstat

检查结果通常可以从以下接口查看:

cat /sys/block/md0/md/sync_action
cat /sys/block/md0/md/mismatch_cnt

check 会读取并比较阵列内容,可能占用大量 I/O。mismatch_cnt 非零表示发现不一致,但它不自动告诉你哪一份数据具备业务意义,也不等于已经定位了具体文件。

某些系统通过定时任务或 systemd timer 周期性执行阵列检查。应确认检查任务存在、能够完成,并且不会与业务高峰和重建任务产生不可接受的竞争。


15. 性能:RAID 级别只是起点

RAID 性能由以下因素共同决定:

  • RAID 级别和布局;
  • 条带大小(chunk size);
  • 文件系统块大小和分配策略;
  • 顺序或随机 I/O;
  • 读写比例;
  • 队列深度;
  • 磁盘介质和控制器;
  • 重建、校验或 reshape 是否正在运行。

例如 RAID5 的随机小写容易触发部分条带的读-改-写;顺序大写更容易形成完整条带。RAID1 读取可以从多个副本分配请求,但实际调度行为不能简单等同于“读取速度翻倍”。

使用 iostat 检查性能:

iostat -xz 5

重点关注:

  • r/sw/s:读写请求速率;
  • rkB/swkB/s:吞吐;
  • await:平均请求等待时间;
  • aqu-sz:平均队列长度;
  • %util:设备忙碌程度。

%util 高不必然等于性能故障,NVMe、并发队列和设备实现会影响它的解释。必须结合延迟、吞吐、队列深度和业务请求时间判断。


16. 一个可重复的实验方式:使用 loop 设备

不想直接操作真实磁盘时,可以用文件创建实验阵列。但 loop 设备只能验证命令、状态和部分故障流程,不能代表真实磁盘的性能、掉电行为或介质错误。

示例:

mkdir -p ~/mdadm-lab
for n in 1 2; do
    truncate -s 512M ~/mdadm-lab/disk${n}.img
done

loop1=$(sudo losetup --find --show ~/mdadm-lab/disk1.img)
loop2=$(sudo losetup --find --show ~/mdadm-lab/disk2.img)

sudo mdadm --create /dev/md99 \
    --metadata=1.2 \
    --level=1 \
    --raid-devices=2 \
    "$loop1" "$loop2"

cat /proc/mdstat
sudo mdadm --detail /dev/md99

sudo mkfs.ext4 /dev/md99
sudo mkdir -p /mnt/mdadm-lab
sudo mount /dev/md99 /mnt/mdadm-lab
echo test | sudo tee /mnt/mdadm-lab/example.txt
sync

模拟成员故障:

sudo mdadm --manage /dev/md99 --fail "$loop1"
cat /proc/mdstat

实验结束时:

sudo umount /mnt/mdadm-lab
sudo mdadm --stop /dev/md99
sudo losetup -d "$loop1"
sudo losetup -d "$loop2"

如果实验中途停止阵列或删除 loop 设备,应先确认没有挂载点和正在运行的 I/O。否则实验本身会制造与真实磁盘故障相似的错误。


17. 生产环境中的验证顺序

一次阵列变更或故障处理,至少应形成以下闭环:

识别设备
  → 查看 md 状态
    → 查看内核日志和 SMART
      → 执行最小必要变更
        → 观察 recovery/resync/reshape
          → 验证阵列状态
            → 验证文件系统和挂载
              → 验证业务读写与告警

例如更换成员后,不应只看到 mdadm --add 命令返回成功就结束。还应确认:

sudo mdadm --detail /dev/md0
cat /proc/mdstat
sudo dmesg -T | tail -n 100
df -h /data
sudo systemctl status mdmonitor.service

最终目标不是“命令执行成功”,而是:

  • 阵列成员数量正确;
  • 没有 failed 或 removed 成员;
  • recovery 已完成;
  • 文件系统仍然挂载并可读写;
  • 监控能够报告下一次故障;
  • 替换下来的旧盘没有被误认为新盘;
  • 配置已写入 initramfs 和系统持久化配置。

18. 必须牢记的边界

RAID 级别提供的是明确的块级故障模型:

  • RAID0:扩大容量和条带化,但没有容错;
  • RAID1:保存多个副本,容量按一份计算;
  • RAID5:一个校验,可容忍一个成员故障;
  • RAID6:双校验,可容忍两个成员故障;
  • RAID10:镜像与条带组合,容错取决于故障是否集中在同一镜像组。

mdadm 负责管理阵列,而不是替代备份、文件系统检查、SMART 监控、灾难恢复演练或应用级数据校验。真正可靠的存储方案必须同时回答四个问题:

  1. 单块磁盘故障时,阵列能否继续服务?
  2. 重建期间再次故障时,阵列是否仍有容错余量?
  3. 阵列元数据、文件系统和启动配置损坏时,如何恢复?
  4. 误删除、逻辑损坏和主机整体丢失时,备份在哪里?

只有把这些问题分别验证过,/dev/md0 才不只是一个“看起来正常”的块设备,而是可监控、可重建、可恢复的存储组件。


系列导航与关联阅读

官方资料

本文依据 Linux 内核、systemd 与主流发行版官方文档重新梳理;正文与实验由 WR BLOG 编写。