上周帮同事处理一台跑了四年的老数据库机,系统盘是两块 SATA 做的 RAID1,其中一块亮了黄灯。他第一反应是「赶紧把坏盘拔了换块新的」,被我拦住了——那台机器上 mdadm 的监控邮件根本没配,坏的到底是哪块、阵列现在是不是已经降级、换上去的新盘会不会把数据搞乱,全没谱。最后我们按流程走完,半个钟头搞定,没丢一条数据。
这事儿让我觉得,mdadm 这种「古董级」工具还是值得认真写一遍。它确实不新,但胜在几乎所有发行版自带、文档全、排错经验多。ZFS 和 LVM 很香,可真到给一台 CentOS 7 老机器加镜像盘、或者纯 Linux 环境下不想引入 ZFS 许可和内核模块麻烦时,mdadm 依旧是那个闭眼都能用的选择。
先想清楚要哪种级别
RAID 级别不是越高级越好,得看你的盘和你的命。
- RAID1(镜像):两块盘互为镜像,写性能基本不变,读可以并发。最适合作系统盘、或者两块大盘做数据安全。坏一块照常跑,这是它最大的价值。
- RAID0(条带):纯提速,没冗余。任何一块坏全盘完蛋。我只会在临时计算、明确能重算的数据上用它,生产环境碰都不碰。
- RAID5:N 块盘里拿一块的容量做校验,允许坏一块。读快、写因要计算校验稍慢。但有个老生常谈的问题——重建时整列都在读,第二块盘如果也藏了坏扇区(URE),重建就直接挂。容量越大越危险,我一般只在 4T 以下、且能接受风险的场景用。
- RAID10:先做镜像再做条带,至少要 4 块盘。坏一块还能跑,坏的不在同一镜像对里能坏两块。性能和安全都好,代价是容量只有一半。数据重的业务我倾向它而不是 RAID5。
说句实话,现在硬盘便宜,RAID10 的「浪费」很多时候比一次数据事故便宜得多。别为了省那点容量去赌 RAID5 的重建运气。
建阵列之前:分区对齐这一关
直接用整块裸盘(/dev/sdb)建 RAID 也行,但我不推荐。给每块盘先分一个对齐到 1MiB 的分区,好处是换盘时新盘大小略有差异也不会卡死,而且分区表里能看清这是块成员盘。
# 用 parted 给新盘建一个对齐分区(以 /dev/sdb 为例)
parted -s /dev/sdb mklabel gpt
parted -s /dev/sdb mkpart primary 1MiB 100%
parted -s /dev/sdb align-check optimal 1
# 把这块盘的分区表完整复制到另一块同型号盘,省得手工对齐
sfdisk -d /dev/sdb > /tmp/sdb.layout
sfdisk /dev/sdc < /tmp/sdb.layout
分区类型建议设成 Linux RAID(fd00,用 sgdisk -t 1:FD00 /dev/sdb 或者 parted 里 set 1 raid on)。设了类型,系统启动扫描时才会把它认作阵列成员,少很多玄学问题。
真正建阵列
假设 /dev/sdb1 和 /dev/sdc1 是要做镜像的两块。建 RAID1:
# 建一个名叫 data 的 RAID1,两块盘,元数据用 1.2(默认即可)
mdadm --create /dev/md0 --name=data --level=1 --raid-devices=2 \
/dev/sdb1 /dev/sdc1
# 建 RAID10(4 块盘,近布局)
mdadm --create /dev/md0 --name=data --level=10 --raid-devices=4 \
/dev/sdb1 /dev/sdc1 /dev/sdd1 /dev/sde1
--create 之后立刻去看一眼状态,别等:
cat /proc/mdstat
# 应该能看到 md0 : active raid1 sdb1[0] sdc1[1]
# 刚建好时 resync 进度会从 0% 往上走,这是在做初始同步
新建完阵列还没数据,这块 resync 是在「把镜像两侧对齐」,其实没啥可同步的,但 mdadm 默认会跑完。想跳过初始同步(确认盘是干净的),可以加 --assume-clean,不过我一般不敢用,除非我百分之百清楚这两块盘上一无所有。
把阵列「登记」下来,否则重启就找不到了
这一步是新手最容易漏的。建完阵列不写配置文件,机器一重启,/dev/md0 可能变成 md127 甚至不自动组装,系统起不来。
# 把当前运行中的阵列信息扫进 mdadm.conf
mdadm --detail --scan >> /etc/mdadm/mdadm.conf
# Debian/Ubuntu 是 /etc/mdadm/mdadm.conf,CentOS/RHEL 是 /etc/mdadm.conf
# 更新 initramfs,让启动早期就能组装根阵列(系统盘做 RAID 时这条关键)
update-initramfs -u # Debian 系
dracut -f # RHEL 系
# 确认阵列名在配置里
grep ARRAY /etc/mdadm/mdadm.conf
如果系统盘就在 RAID1 上,还得到 GRUB 里确认启动设备指向阵列(通常是 /dev/md0 或它的分区),不然重启会卡在 initramfs。这事我在虚拟化环境踩过一次,最后靠 ISO 进 rescue 模式把 grub 重装才救回来。所以改完配置务必留一张带外管理(IPMI/iDRAC)的后路。
给阵列套上文件系统
阵列本身是个块设备,上面还得建文件系统。XFS 对大文件、大容量友好,ext4 更稳更通用,按场景挑。
mkfs.xfs -L data /dev/md0
mkdir -p /data
mount /dev/md0 /data
# 写进 /etc/fstab,用 UUID 更稳
blkid /dev/md0
echo 'UUID=xxxx-xxxx /data xfs defaults 0 0' >> /etc/fstab
一个小细节:/etc/fstab 里挂 RAID 设备建议用 UUID=,别用 /dev/md0 这种设备名。设备名在多次重启、热插拔后可能漂移,UUID 不会。
打开 write-intent bitmap,重建能快一倍
这是文档里提得少、但价值极大的一招。bitmap 是一小块记录「哪些区域正在写」的日志。平时几乎不占空间,但阵列降级后重建时,mdadm 只同步那些真被写过、可能不一致的区块,而不是整盘重扫。对几 T 的阵列,重建时间能从几小时缩到几十分钟。
# 给运行中的阵列加一个内部 bitmap
mdadm --grow /dev/md0 --bitmap=internal
# 查看是否生效
mdadm --detail /dev/md0 | grep -i bitmap
# 应该看到 Intent Bitmap : internal
bitmap 有内部(internal,存在阵列自身)和外部(文件)两种。绝大多数情况用 internal 就够。注意:bitmap 存在阵列里时,对写性能有一点点影响(每次写前更新位图),生产上这点代价换来重建速度,划算。
盘坏了,怎么热替换
回到开头那台机器。确认某块盘坏了(smartctl 报大量重分配扇区、或者 /proc/mdstat 里它已经是 F 标记),流程是:
# 1. 先看清楚阵列和成员状态
mdadm --detail /dev/md0
cat /proc/mdstat
# 2. 把坏盘标记为 faulty(如果它还没自动降级)
mdadm /dev/md0 --fail /dev/sdb1
# 3. 从阵列移除
mdadm /dev/md0 --remove /dev/sdb1
# 4. 物理换盘(支持热插拔的机器直接拔插;不支持就挑维护窗口关机换)
# 5. 新盘分好区后加回阵列
parted -s /dev/sdb mklabel gpt
parted -s /dev/sdb mkpart primary 1MiB 100%
mdadm /dev/md0 --add /dev/sdb1
# 6. 盯重建进度
watch -n 5 'cat /proc/mdstat'
重建期间阵列是降级运行的,这一时段再坏一块同组盘就真没救了。所以换盘别拖,坏盘确认后尽快换、尽快等重建完。我习惯把重建进度重定向到日志,方便事后复盘:
cat /proc/mdstat | ts >> /var/log/md-rebuild.log &
ts 来自 moreutils,给每行加时间戳,没有就自己套 date。
让阵列自己「喊疼」:监控配置
前面说那台机器没配监控邮件,是这次最该补的。mdadm 自带监控守护,能在一块盘 fail、阵列降级时发邮件或跑脚本:
# /etc/mdadm/mdadm.conf 里加这两行
MAILADDR ops-alert@example.com
# 程序默认就用 mdadm --monitor,systemd 下一般是 mdmonitor.service
systemctl enable --now mdmonitor
# 手动测一下监控能不能发信(把 md0 里一块盘 fail 再恢复,看邮箱)
光靠邮件还不够。我一般再叠加一层 SMART 自检,在盘彻底坏之前就预警:
# 开 smartd 后台扫描
systemctl enable --now smartd
# /etc/smartd.conf 里对数据盘加 -a -m ops-alert@example.com -s (S/../.././02|00)
# 意思是每天凌晨 2 点跑一次短自检
还有更主动的——定期做一致性检查(scrub),把阵列两侧数据逐块比对,把「默默出错但还没 fail」的bit找出来:
# 触发一次全量校验(写入 mismatch_cnt)
echo check > /sys/block/md0/md/sync_action
# 等 /proc/mdstat 走完,看不一致的块数
cat /sys/block/md0/md/mismatch_cnt
# 非零说明有不一致;raid1 可以安全 repair,raid5 慎用 repair(可能写错一侧)
mismatch_cnt 非零在 RAID1 上通常无害,可以 echo repair > /sys/block/md0/md/sync_action 让它以镜像源为准修一遍。RAID5 上则要小心,repair 会按校验重新算,万一校验本身那次是脏的,反而把好的覆盖掉——这种我倾向先备份再决定。
那些没人提前告诉你的坑
RAID 不是备份。 阵列防的是「一块盘物理损坏」,防不了 rm -rf、防不了勒索软件、防不了写错库。我见过有人把 RAID 当备份,结果运维脚本一个 bug 把整列数据覆盖,RAID 稳稳地、同步地把两份都覆盖了。冗余和备份是两件事,别混。
RAID5 的写漏洞(write hole)。 突然掉电时,一次写可能数据写完了、校验还没写,恢复后这 stripe 数据就撕裂了。企业级带 BBU 的阵列卡能靠电容撑住把缓存刷完,纯软 RAID 没有这层保护。要消除它得上 RAID1/RAID10,或者上 ZFS 那种有校验和的。
degraded 不是 failed,但比 failed 危险。 阵列降级还能跑,很多人就「先跑着,下周再换」,结果下周第二块也挂了。降级状态是黄金抢救窗口,别浪费。
换上去的新盘必须比旧盘大(或相等)。 用整盘建阵列时无所谓,用分区建时如果新盘柱面略小,加不回去。所以前面强调先分区、对齐、用 UUID。
bitmap 别忘了开。 我接手过一台 RAID5 重建花了 6 个多小时,后来发现从没开 bitmap,重新开之后同规格重建 40 分钟。这差距是真实存在的。
--add 之前确认新盘没旧元数据。 一块曾经在别处做过阵列成员的盘,直接 --add 会被 mdadm 拒绝(它认出旧 UUID),或者更糟——把旧阵列的信息带进来。先 mdadm --zero-superblock /dev/sdb1 清掉旧超级块再添加。
NVMe + SATA 混搭做 RAID1 可以,但注意 write-mostly。 如果你用一块慢 HDD 给快 SSD 做镜像备份(比如读多写少、HDD 只防灾难),给 HDD 设 write-mostly 能让读都走 SSD,写两边:
mdadm /dev/md0 --add /dev/sdc1 --write-mostly /dev/sdc1
收个尾
mdadm 这套东西,难不在命令多,在于「盘要坏的那一刻你知不知道该敲哪几行、换完能不能自己起来」。我的习惯是每台做了软 RAID 的机器,交付前都在测试环境真拔一次盘、看重建、看监控邮件到底收没收到——别等生产环境第一次掉盘才发现自己邮件地址写错了。
工具本身几十年没大变,意味着你今天学的排错经验,五年后照样用得上。这反而是它比很多「新一代」存储方案更让人安心的地方。

