服务器 SSD 越用越慢:TRIM 未启用的成因、DISC-MAX 判读与 fstrim.timer 正确配置

服务器磁盘跑了一年突然变慢:TRIM 没开,SSD 在替你默默还债

我手上一台 Debian 服务器,装的是 SSD,跑了一年多。最初 dd 测试顺序写能到 400MB/s,后来慢慢掉到 150MB/s,再后来 iostat 里的写入延迟(w_await)从 1ms 涨到了 30ms 以上。网站后台保存文章要转好几秒,MySQL 的刷盘也开始拖后腿。

我先怀疑是磁盘要坏了,结果 smartctl 显示一切正常。真正的原因是:这台机器的 TRIM 从来没有启用过。

这篇文章讲清楚四件事:TRIM 到底是什么、没开会发生什么、怎么判断你的环境支不支持、以及正确开启方式(含云服务器这个特殊场景)。

一、TRIM 到底在解决什么问题

要理解 TRIM,得先理解 SSD 的写放大(write amplification)。

机械硬盘里,「删除文件」是真的把那一块标记成空闲,下次可以直接覆盖写。但 SSD 不一样:闪存的最小写入单位是「页」(page,通常 4KB),而最小擦除单位是「块」(block,通常 512KB 甚至更大)。闪存只能以块为单位擦除,写之前必须先擦。

于是就有了这个尴尬局面:

  • 你删掉了一个 4KB 的文件,文件系统在目录里把它标记为「空闲」
  • 但 SSD 主控完全不知道这件事——SSD 只知道「这个页里还有数据」
  • 下次要写这块时,主控无法直接擦除整块(因为块里还有其他有效页),只能把整块的有效数据读出来、写到新的块、再擦除旧块
  • 结果:你只写了 4KB,SSD 实际搬运+写入了几百 KB,寿命和性能双双受损

TRIM 就是操作系统主动告诉 SSD 主控「这些块的数据我不要了,你可以提前擦」。这样主控能在空闲时做垃圾回收(GC),真正写入时就有干净的空块可用,避免「边搬边写」。

状态主控的视角写入时的行为
无 TRIM已删除的数据仍然「有效」读旧块 → 合并 → 写新块 → 擦旧块(写放大 3-10 倍)
有 TRIM已知该块无效直接擦除后写入(写放大接近 1 倍)

二、不启用 TRIM 的真实后果

很多人以为 TRIM 只影响寿命,其实性能下降往往先于寿命问题暴露出来,而且症状很有特征:

  • 顺序写性能随使用时间线性下降——新盘快,越用越慢,格式化之后又能恢复
  • iostat -x 里的 w_await 持续偏高,但 %util 看着不高(因为瓶颈在主控内部搬运,不是接口)
  • 小文件随机写特别慢(正好是网站最常见的 IO 模式:日志、session、缓存)
  • 删除大量文件后反而更慢——这是 TRIM 缺失最反直觉的症状,因为「删除」把更多块变成了「需要搬运」的状态

我自己这台的实测数据(同一台 VPS,同一份测试命令):

指标启用 TRIM 前启用 TRIM 后(一周)
顺序写(dd 1M bs)154 MB/s389 MB/s
随机写 4K3.1 MB/s21 MB/s
w_await(iostat)28 ms3.4 ms
删除 5GB 文件后再写明显卡顿几乎无感

注意最后一行——随机写提升了近 7 倍,这才是对网站最实际的影响。

三、先判断你的环境支不支持 TRIM

不是所有环境都能开 TRIM。按这个顺序确认:

第 1 步:确认是 SSD 还是虚拟磁盘

lsblk -d -o NAME,ROTA,SIZE,MODEL
# ROTA=0 是 SSD,ROTA=1 是机械盘

第 2 步:确认磁盘是否支持 discard 操作

# 方法一:看 lsblk 的 DISC-GRAN / DISC-MAX 列
lsblk -D -o NAME,DISC-ALN,DISC-GRAN,DISC-MAX
# DISC-GRAN 和 DISC-MAX 非 0 才说明支持 TRIM

# 方法二:用 hdparm 直接问设备
hdparm -I /dev/sda | grep -i trim
# 期望看到:* Data Set Management TRIM supported

第 3 步(关键):确认是哪种盘

环境TRIM 支持情况推荐做法
自购 SATA/NVMe SSD 直通物理机支持fstrim.timer 定期执行
VPS 虚拟磁盘(virtio/scsi)取决于宿主机先测 DISC-MAX,不支持则别折腾
云厂商云盘(阿里云 ESSD / AWS gp3)部分支持查厂商文档,多数支持 online discard
硬件 RAID 卡(未开 JBOD)通常不支持几乎无法透传,看 RAID 卡是否支持 SSD 直通
LVM / 加密层(LUKS)上层需要逐层打通见第四节

如果你的 DISC-MAX 是 0,说明虚拟层根本没透传 TRIM,那就到此为止——不管怎么配都不会生效。

四、两种开启方式,别选错

TRIM 有两种实现路径,很多人只知道第一种,而第一种恰恰不推荐用于服务器。

方式一:挂载时带 discard(online discard)

# /etc/fstab
UUID=xxxx-xxxx / ext4 defaults,discard,noatime 0 1

这种方式每个删除操作立即同步触发一次 TRIM 命令,理论上「最实时」,但实际问题是:

  • 每次 rm 都要等磁盘确认,删除大量文件时会有明显卡顿(我实测删 10 万个日志文件时,整整卡了 40 多秒)
  • 不是所有 SSD 都正确处理高频 TRIM 命令,部分老固件会因此掉速
  • 加重主控负担,反而可能降低寿命

所以:服务器上不推荐 online discard。

方式二:定期 fstrim(推荐)

内核和文件系统记录「哪些块被释放了」,由 fstrim 在低峰期一次性批量下发。Debian/Ubuntu 上直接启用系统自带的 timer:

# 先手动跑一次,看效果和耗时
fstrim -av
# 输出示例:
# /boot: 128 MiB (134217728 bytes) trimmed on /dev/sda1
# /: 12.4 GiB (13314398618 bytes) trimmed on /dev/sda2

# 启用自动执行(默认每周一次,时间由 systemd 随机化避免同时打盘)
systemctl enable --now fstrim.timer
systemctl list-timers fstrim.timer --no-pager

看 fstrim -av 的输出能直接判断状态:

  • 输出显示 trimmed 了多少 MiB/GiB:TRIM 真的在干活,而且这些之前是没被回收的
  • 输出 0 B trimmed:要么已经回收干净了,要么下层不支持 TRIM 静默忽略(需要回头用第三节的方法确认)
  • 报 fstrim: /: the discard operation is not supported:明确不支持,别再折腾

调整执行频率

默认每周一次对大多数站点够用。如果 IO 量大、删除频繁(比如每天清理日志、频繁上传图片删图片),可以改成每天:

mkdir -p /etc/systemd/system/fstrim.timer.d
cat > /etc/systemd/system/fstrim.timer.d/override.conf <<'EOF'
[Timer]
OnCalendar=
OnCalendar=daily
Persistent=true
RandomizedDelaySec=2h
EOF

systemctl daemon-reload
systemctl restart fstrim.timer
systemctl list-timers fstrim.timer --no-pager

Persistent=true 保证关机错过的任务下次开机补跑;RandomizedDelaySec 避免多台机器同一时刻一起打盘。

五、LVM 和加密层:TRIM 需要逐层打通

如果你的磁盘上叠了 LVM 或者 LUKS 加密,TRIM 必须每一层都显式允许,否则会被中间层吃掉。这是最容易漏的一步。

# 1. 检查 LVM 是否允许传递 discard
lvs -o name,discards
# 期望看到 discards=passdown 或 nopassdown

# 修改允许
lvmconfig --type default activation/issue_discards
# /etc/lvm/lvm.conf 里设置:
#   issue_discards = 1
# 并且:
lvchange --discards passdown /dev/vg0/lv_data

# 2. 如果是 LUKS 加密,在 /etc/crypttab 里加 discard
# 格式:  none luks,discard
cat /etc/crypttab

# 3. 最后确认挂载层
findmnt -O discard
mount | grep -o 'discard'

⚠️ 加密层开 discard 有一个真实的安全权衡:TRIM 会向底层泄露「哪些块是空的」这一信息,理论上可以被用来推断加密数据的分布。自用服务器上这个风险可以接受,但如果是涉及敏感数据的场景,需要自己权衡。折中方案是:加密层不开 discard,只在上层的文件系统定期 fstrim——但这样中间层仍然会挡掉,效果有限。

六、日常监控:别等变慢了才发现

配好之后要能持续看到状态。三个命令足够:

# 1. 估算 SSD 剩余寿命(磨耗指标)
smartctl -a /dev/sda | grep -iE 'Percentage_Used|Wear_Leveling|Media_Wearout'
# Percentage_Used 是已消耗的寿命百分比,NVMe 上很有参考价值

# 2. 看写入总量(TBW 对比)
smartctl -a /dev/sda | grep -iE 'Total_LBAs_Written|Data_Units_Written'

# 3. 看实时写入延迟是否正常
iostat -x 5 3 | grep -E 'Device|sda'
# 关注 w_await(平均写等待毫秒)和 aqu-sz(队列深度)

建议把第 1 条做成每周 cron,写入一个文件,这样能看出磨耗速度。如果 Percentage_Used 在短时间内快速上升(比如一个月涨 5%),说明写放大很严重,大概率就是 TRIM 没生效。

七、什么时候不该开 TRIM

说点反面的:

  • 下层明确不支持(DISC-MAX=0、RAID 卡直通不了):配了也白配,fstrim 会静默忽略或报错,别浪费时间。
  • 机械硬盘:TRIM 对 HDD 无意义,反而可能触发无谓的 IO。
  • 老旧的 USB 转 SATA 桥接盘:很多桥接芯片不支持 TRIM 透传,还会导致命令超时。
  • 业务对稳定延迟极度敏感且无法安排低峰期:fstrim 执行时会占用主控,虽然实测影响很小,但极端的实时场景可以手动在维护窗口跑。

八、小结

SSD 变慢这件事,十个里有七八个是 TRIM 没开。判断路径很清晰:lsblk -D 看 DISC-MAX 确认支持 → 用 fstrim.timer 定期执行(别用 online discard)→ LVM/LUKS 逐层放行 → smartctl 跟踪磨耗。整个过程十分钟,换来的是随机写性能数倍提升和明显延长的磁盘寿命。

对个人站长来说,最实际的收益是:后台保存文章不再转圈、​MySQL 刷盘不再拖慢 TTFB。这和换一块新盘的效果差不多,但一分钱不花。

Last modification:September 29th, 2026 at 01:25 pm

Leave a Comment