磁盘要坏之前,SMART里有五个数字会先变

为什么机房不会替你发现硬盘要坏

去年冬天,一台跑了三年的博客服务器突然在凌晨两点挂掉。重启之后 SSH 能进,但 MySQL 起不来,报的是 InnoDB: Error: page ... log sequence number。拆开看日志才发现,早在两个月前内核就已经在报 ata3.00: failed command: READ FPDMA QUEUED,只是没人看。机房那边监控的是「机器有没有 ping 通」,硬盘的坏道增长、重映射扇区数量,从来不在他们的报表里。

这件事之后我给所有机器加了一套磁盘健康巡检。这篇文章把整套做法写下来,包括 SMART 到底怎么看、哪些属性是真正的预警信号、为什么 NVMe 和机械盘要分开对待、TRIM 在 LVM 与文件系统上的正确传递方式,以及一套可以直接抄的月度检查脚本。

一、先确认你的盘支不支持 SMART

不是所有盘都能报告健康信息。虚拟化环境里的虚拟磁盘、部分廉价 USB 硬盘盒走的是 SCSI 转接,SMART 指令根本到不了物理盘。先跑一条命令确认:

smartctl -i /dev/sda

输出里要看到两行关键信息:SMART support is: Available 和 SMART support is: Enabled。如果第一行是 Unavailable,后面所有检查都没有意义,只能靠 dmesg 里的 I/O 错误和文件系统自检兜底。如果可用但未启用,一条命令打开:

smartctl -s on /dev/sda

Debian 上装的是 smartmontools。装完默认会有一个 smartd 服务,很多人装了就不管,其实它才是真正能提前报警的东西。默认配置基本不管事,需要在 /etc/smartd.conf 里改成这样一条通用规则:

/dev/sda -a -o on -S on -s (S/../.././02|L/../../6/03) -m root -M exec /usr/share/smartmontools/smartd-runner

含义是:开启全部属性监控、开启属性自动保存与离线收集、短自检每天凌晨两点、长自检每周六凌晨三点、发现问题发邮件给 root。改完 systemctl restart smartd,再 systemctl status smartd 看有没有报设备名不存在的错。

二、真正要盯的属性只有五六个

很多人打开 smartctl -a 看到几十行属性就放弃了。实际上对机械盘来说,值得盯的只有下面几个,其余大多是厂商自定义,含义不透明:

smartctl -A /dev/sda

5 Reallocated_Sector_Ct(重映射扇区数)。这是最重要的一个。盘出厂时留有备用扇区,读写出错就把坏扇区映射到备用区。这个值从 0 变成非 0,说明盘已经出现了物理坏道;从几十涨到几百,说明在快速恶化。判据不是看它是不是 0,而是看它的增长速度:稳定在 8 三个月不变可以继续用,一周涨 20 就必须立刻换盘。

187 Reported_Uncorrect(无法纠正的错误数)。这个数只要大于 0 就意味着有过读不出来的扇区。它和 5 的区别是:5 是「已经救回来了」,187 是「救不回来」。大于 0 就应该安排更换计划。

197 Current_Pending_Sector(待定扇区)。表示读写时出现了错误、但还没决定是重映射还是放弃的扇区。这个值反复在 0 和几之间跳,通常是盘面开始不稳的前兆。

198 Offline_Uncorrectable。离线自检时发现无法纠正的扇区数。它一般和 197 同步变化。

199 UDMA_CRC_Error_Count(接口 CRC 错误)。这个属性最容易被误读——它不是盘的问题,是线的问题。SATA 线松动、线材质量差、背板接触不良都会让它增长。看到这个数涨,第一件事是换线、换插槽,而不是换盘。真实事故里相当比例的「硬盘坏了」其实是 SATA 线氧化。

剩下的 Power_On_Hours、Temperature_Celsius 作为背景信息看。机械盘长期在 45 度以上寿命会明显缩短;NAS 机箱里硬盘挨着装,夏天很容易过 50 度,这时候加个风扇比换盘便宜得多。

三、NVMe 与 SSD 是另一套指标

SSD 没有机械结构,上面那些属性的含义完全不同,甚至不存在。用 smartctl -a /dev/nvme0 会看到另一种格式的输出,重点看这几项:

Percentage Used:                    12%
Data Units Written:                 84,312,004 [43.1 TB]
Media and Data Integrity Errors:    0
Error Information Log Entries:      0
Available Spare:                    100%
Available Spare Threshold:          10%

Percentage Used 是厂商按写入量估算的寿命消耗,不是精确值,但 80% 以上就该规划更换。Available Spare 掉到阈值附近意味着备用块快用完了。Media and Data Integrity Errors 只要非 0,就是数据完整性出过问题,这是 SSD 最严重的信号,比 Percentage Used 高得多还危险。

SATA SSD 用 smartctl -a /dev/sda 看 241 Total_LBAs_Written(有的厂商叫 Host_Writes_32MiB),单位各厂不一,换算时要用 smartctl -l devstat 交叉核对,别直接按扇区数乘 512 算——这是很多监控脚本算出天文数字的原因。

还有一个常见误区:SSD 的 SMART 自检(short/long self-test)意义不大。SSD 的故障往往是主控或固件突然失效,SMART 属性一切正常,下一秒就掉盘。所以 SSD 的正确策略是:不指望预警,靠冗余。RAID1 或者每天备份到另一台机器,比任何监控都可靠。

四、TRIM 传不到盘上,是最隐蔽的性能坑

SSD 删除文件时,如果操作系统不发 TRIM 指令,盘主控不知道这些块已经没用了,写放大就会上升,长期表现为写入速度下降。检查方法:

systemctl status fstrim.timer
fstrim -av

Debian 默认有 fstrim.timer,每周执行一次。fstrim -av 会列出每个挂载点回收了多少字节。如果输出里某个分区被跳过,提示 the discard operation is not supported,说明 TRIM 没传到盘上。

在有 LVM 的机器上,这一层最容易断。链路上有三个开关,任何一个关着都不行:

第一,LVM 配置里 issue_discards 必须是 1。它在 /etc/lvm/lvm.conf 里,默认是 0(关闭)。关闭时,即使文件系统发了 discard,LVM 层也会把它吃掉。改完要重建 initramfs,否则早期启动阶段仍然用旧配置:

update-initramfs -u

第二,文件系统挂载参数要有 discard,或者靠 fstrim 定时任务。两者选一个:实时 discard 每次删除都发指令,对老 SSD 有性能开销,现在更推荐定时 fstrim。注意 fstrim 只对支持 discard 的文件系统有效,ext4、XFS、Btrfs 都支持。

第三,加密层不能吞掉 discard。如果用 LUKS,默认是不允许 discard 穿透的(因为会泄露文件系统结构信息),需要在 /etc/crypttab 里加 discard 选项。这是个安全与性能的权衡:单机自用可以加,多租户环境不建议。

另外要提醒一点:不要给 RAID 卡后面的盘开实时 discard。硬件 RAID 卡对 TRIM 的支持参差不齐,开了之后容易触发固件 bug。这种情况用 fstrim 定时任务,并确认 RAID 卡固件版本支持 passthrough。

五、一套能直接用的月度巡检脚本

手动看 SMART 坚持不了三个月。下面这个脚本扫描所有盘,只在发现异常时输出,可以直接挂到 cron 里,也可以人工月度跑一次:

#!/bin/bash
# /usr/local/bin/disk-health.sh
set -u
WARN=0
for dev in $(lsblk -dn -o NAME,TYPE | awk '$2=="disk"{print "/dev/"$1}'); do
    info=$(smartctl -H -A "$dev" 2>/dev/null) || continue
    # 整体健康结论
    if ! echo "$info" | grep -qE 'PASSED|OK'; then
        echo "CRIT $dev 整体健康检查未通过"
        WARN=1
    fi
    # 逐项阈值
    for pair in "5:1" "187:1" "197:1" "198:1" "199:1"; do
        attr=${pair%%:*}; lim=${pair##*:}
        val=$(echo "$info" | awk -v a="$attr" '$1==a{print $10}')
        if [ -n "$val" ] && [ "$val" -gt "$lim" ] 2>/dev/null; then
            echo "WARN $dev 属性 $attr = $val (阈值 $lim)"
            WARN=1
        fi
    done
    temp=$(echo "$info" | awk '$1==194{print $10}')
    if [ -n "$temp" ] && [ "$temp" -gt 50 ] 2>/dev/null; then
        echo "WARN $dev 温度 ${temp}C"
        WARN=1
    fi
done
[ "$WARN" -eq 0 ] && echo "OK 所有磁盘指标正常"
exit $WARN

几个细节值得说明。lsblk -dn 只列整盘不列分区,避免对着 /dev/sda1 调 smartctl 报错。awk '$1==a{print $10}' 取的是 RAW_VALUE 列,这要求 smartctl 输出是默认的表格格式——如果你在 /etc/smartd.conf 或别名里改过 -j,列号会变,建议改成 smartctl -A -j 直接解析 JSON,稳定性好得多。

另外这个脚本对属性 5 用阈值 1 判定,实际上会有点吵:盘用了五年、重映射了 2 个扇区,不一定是故障。更聪明的做法是把每次的 RAW_VALUE 存下来,比较变化量:

val=$(echo "$info" | awk '$1==5{print $10}')
last=$(cat /var/lib/disk-health/sda_5 2>/dev/null || echo "$val")
echo "$val" > /var/lib/disk-health/sda_5
if [ "$val" -gt "$last" ] 2>/dev/null; then
    echo "WARN /dev/sda 重映射扇区从 $last 增长到 $val"
fi

这样只报「新增长」,历史欠账不会天天烦你。/var/lib/disk-health/ 目录要先 mkdir -p。

六、发现坏盘之后的正确动作顺序

脚本报警不等于立刻关机换盘。真实处置有严格顺序,走错一步可能让「可以救」变成「救不回」:

第一步,先确认备份是最新的。不要先跑任何修复工具。很多人在坏盘上直接 fsck -y,结果把还能读的数据改得面目全非。先看备份时间戳,如果超过一天,立刻用 rsync -a --partial 或 ddrescue 把还能读的数据抽出来。

第二步,只读不写地镜像整盘。用 ddrescue 而不是 dd,它会记录已经读过的区域,能中断续传,遇到坏块会自动跳过而不是卡死:

ddrescue -d -r3 /dev/sda /mnt/backup/sda.img /mnt/backup/sda.map

参数 -r3 表示坏块重试 3 次。-d 是直接访问设备绕过缓存。

第三步,判断是不是线的问题。如果 SMART 里只有 199 CRC 错误在涨,其他都正常,换一根 SATA 线和换一个插槽,再用 smartctl -t long 跑一次完整自检。很多「硬盘故障」到这一步就解决了。

第四步,才考虑更换。同一槽位上新盘跑一次 smartctl -t short 确认正常,再迁移数据。旧的坏盘不要立刻格式化——至少保留一个月,直到确认新盘稳定。

七、几个必须避开的误区

误区一:SMART PASSED 就代表盘是好的。PASSED 只表示「没有触发厂商设定的失败阈值」,阈值非常宽松。一块已经重映射了 300 个扇区的盘,只要没超过阈值,照样报 PASSED。所以必须逐项看 RAW_VALUE,不能只看结论行。

误区二:硬盘温度不重要。Google 公开过的大规模磁盘统计显示,温度每升高 5 度,年化故障率明显上升。家用服务器塞在书柜里、NAS 硬盘挨着装,夏天 55 度很常见。加一个 12cm 风扇能把温度压到 40 度以下,成本几十块。

误区三:SSD 也用机械盘那套阈值。SSD 的 Reallocated_Sector_Ct 通常恒为 0,看着完美,但主控随时可能挂。SSD 要盯 Percentage Used、Available Spare 和 Media Errors。

误区四:RAID 就等于备份。RAID 只防单盘物理故障,不防误删除、不防勒索软件、不防控制器故障。坏盘重建期间是整个阵列最脆弱的窗口,如果第二块盘有潜在问题,重建过程会直接把它拖死。所以换盘前先把重要数据单独拷一份出来,别急着 mdadm --add。

误区五:等报警了再处理。磁盘故障大多数是渐进的,有几天到几周的可观测窗口。真正致命的是「所有人都不看」。装好 smartd、挂上巡检脚本、把告警发到一个真的会看的邮箱或群机器人里,这三件事的投入产出比比任何优化都高。

八、和备份策略的配合

再好的监控也只是提前量,不能替代备份。我给自己的标准是:任何一块盘坏了,最坏情况下丢失不超过 24 小时的数据,恢复时间不超过 4 小时。为了达到这个标准,需要三层:本地快照(每天)、异地备份(每天增量、每周全量)、离线冷备(每月一份,放在另一个地方)。

还有一个常被忽略的环节:定期做恢复演练。备份脚本天天成功、从来没恢复过,等于没有备份。每个月挑一个不重要的库,从备份里恢复一次,确认能起、数据完整。这一步花二十分钟,能发现备份脚本里所有想当然的错误——比如 mysqldump 忘了 --single-transaction、tar 包漏了隐藏文件、rsync 排除了上传目录里的 usr/。

回到开头那台挂掉的机器:如果当时装了 smartd,两个月前就应该收到「重映射扇区从 0 增长到 34」的告警,换一块盘的代价是一百多块和一个下午,而不是一次数据抢救和三天的心情。

Last modification:October 27th, 2026 at 10:25 pm

Leave a Comment