丹尼拉(Dayfing)
返回文章列表
4,302 字16 分钟

如何选择 NVMe SSD:PCIe 5.0 还是 4.0、DRAM、TLC 还是 QLC、TBW

对大多数工作站和游戏 PC 来说,决定 NVMe SSD 实际体验的不是包装盒上的顺序读写峰值,而是低队列深度下的 4K 随机读取延迟、SLC 缓存写满后的写入速度、是否有 DRAM 或设计良好的 HMB,以及散热。只有当负载持续传输大文件、并且硬盘插在直连 CPU 的 x4 插槽上时,PCIe 5.0 才物有所值。服务器还要额外满足两项要求:以 DWPD 表示的写入耐久度,以及掉电保护。下文逐项说明如何用 fio、smartctl 和 nvme-cli 进行核实。

顺序吞吐量与低队列深度下的 4K 随机性能

顺序读写速度是用大块数据和深队列测出来的,它反映的是复制视频、恢复磁盘镜像或加载模型权重这类场景。编译代码、启动应用、查询放不进内存的数据库则会产生大量小块读取,而且每个线程通常要等上一次读取完成后才发出下一次请求。这时硬盘看到的队列深度只有一到几个命令。

在队列深度为 1 时,IOPS 等于一秒除以平均延迟。如果一次 4 KiB 读取耗时 80 微秒,单个线程就能得到 12,500 IOPS,约合 51 MB/s。延迟减半,这个数字就翻倍;而 PCIe 带宽翻倍对它毫无影响。这就是为什么顺序速度相差很大的两块硬盘,在台式机上用起来可能感觉一样。

选购时请关注 QD1 下的 4K 随机读写结果,以及混合负载下的 p99 或 p99.9 延迟;厂商没有公布的数据,可以用下文的 fio 任务自己测。服务器还需要高队列深度下的 IOPS,因为众多客户端会带来真正的并行。正如 pgvector 混合检索指南 中的情形,放不进内存的 HNSW 索引会把每次查询变成一串相互依赖的随机读取,此时尾部延迟比 GB/s 更重要。

PCIe 代数、通道数与 M.2 插槽连线

PCIe 3.0、4.0 和 5.0 每条通道的速率分别为 8、16 和 32 GT/s,均采用 128b/130b 编码。单方向的上限为每通道 GT/s × 128/130 ÷ 8 GB/s,因此在不计协议开销的情况下,x4 链路的峰值约为 3.9、7.9 和 15.8 GB/s。硬盘和插槽会协商出双方都支持的最高代数和最大宽度:PCIe 5.0 硬盘插在 PCIe 4.0 插槽里只能以 4.0 速度运行,x4 硬盘插在 x2 插槽里只能得到一半通道。

台式机主板上,一部分 M.2 插槽使用 CPU 通道,另一部分挂在芯片组下面。芯片组只有一条通往 CPU 的上行链路,由 USB、SATA、网络以及芯片组下的其他所有插槽共享。主板还会复用通道:占用某个 M.2 插槽,可能会禁用部分 SATA 接口,或让显卡插槽降为 x8。主板说明书中的框图或插槽表会写明每个插槽的连线方式。请在购买前读完它。

把系统盘和最活跃的项目或数据库放在直连 CPU 的插槽上,芯片组插槽则留给游戏和归档数据。对于高码率视频、数据集复制,以及 本地 LLM 硬件指南 中提到的大型模型文件加载,PCIe 5.0 是值得的。对于游戏和日常开发,一块 QD1 延迟低的优质 PCIe 4.0 硬盘通常是更好的选择。

检查协商后的链路

在 sysfs 中比较当前链路和最大链路:

cd /sys/class/nvme/nvme0/device
cat current_link_speed current_link_width max_link_speed max_link_width

插在匹配插槽中的 PCIe 4.0 x4 硬盘会显示 16.0 GT/s PCIe 和宽度 4。如果数值偏低,原因可能是插槽连线、固件设置中强制指定了代数、转接卡,或通道被其他设备共享。

主控、DRAM 缓存与 HMB

主控通过闪存转换层(FTL)把逻辑地址映射到 NAND 页。若采用扁平映射表,每个 4 KiB 页对应一个 4 字节条目,所需空间就是容量的 1/1024,这正是常说的"每 1 TB NAND 配约 1 GB DRAM"的由来。带 DRAM 的硬盘把大部分映射表放在主控旁边,因此在大范围地址上的随机读取依然很快。

无 DRAM 的硬盘使用 Host Memory Buffer(HMB),借用一部分系统内存来缓存映射表。硬盘在 Identify Controller 的 hmpre 字段中以 4 KiB 为单位申请容量,Linux 则通过 nvme.max_host_mem_size_mb 参数加以限制,默认每个控制器 128 MiB。HMB 适合只访问硬盘局部区域的桌面使用模式;当随机 I/O 覆盖数百 GB 时,例如数据库和虚拟机镜像,它的帮助就小得多。

nvme id-ctrl /dev/nvme0 | grep -E '^(mn|fr|hmpre|hmmin|vwc|wctemp|cctemp) '
cat /sys/module/nvme/parameters/max_host_mem_size_mb

无 DRAM 硬盘的 hmpre 不为零,就说明支持 HMB。这块缓冲区不大,但如果系统内存本来就紧张,可以参考 DDR5 内存指南 来规划容量。

TLC、QLC 与 SLC 缓存

TLC 每个单元存储三位,QLC 存储四位。每单元位数越多,单颗芯片容量越大,但编程速度越慢,可承受的编程/擦除次数也越少。消费级硬盘用 SLC 缓存来掩盖慢速写入:新数据先以每单元一位的方式写入,之后再搬到 TLC 或 QLC 中。

缓存容量有限,而且通常是动态的,会随着硬盘变满而缩小。一旦长时间写入把缓存耗尽,速度就会降到缓存外写入速度,QLC 的这个速度往往更低。Ceph 硬件建议 把这种现象称为"断崖"(cliffing),并提醒有限的缓存写满后,持续性能可能大幅下降。

请在评测或规格书中查找特定占用率下的缓存大小和缓存外写入速度。如果你经常一次写入几十 GB,例如导入视频、复制虚拟机镜像或做备份,请选择 TLC 并保留剩余空间。QLC 适合以读取为主的资料库,例如游戏、媒体文件和模型文件。

耐久度:TBW、DWPD 与质保

TBW 是 JEDEC JESD218 定义的耐久度指标:在该硬盘所属类别规定的负载下,主机可写入的十进制 TB 数,前提是硬盘仍保持其容量,并满足错误率、故障率和断电数据保持的要求。DWPD 则把同一额度换算为质保期内每天的全盘写入次数:

DWPD = TBW / (capacity_TB × 365 × warranty_years)
TBW  = DWPD × capacity_TB × 365 × warranty_years

假设一块 2 TB 硬盘额定 1,200 TBW、质保五年,那么 1200 / (2 × 365 × 5) 约等于 0.33 DWPD。每天写入 50 GB 的台式机五年大约用掉 91 TB。每天把整盘重写一遍的数据库则需要更高的额度,Ceph 建议高负载存储池使用 1 DWPD 或以上的硬盘。

主机写入量并不等于 NAND 写入量。小块随机写、硬盘接近写满以及缺少 TRIM,都会提高写入放大系数(WAF)。JEDEC 资料中的一个示例用 TBW < (capacity × NAND P/E cycles) / (2 × WAF) 来估算耐久度。请保留剩余空间、启用 TRIM,并为写入密集的工作选择更大的容量。

许多消费级质保以期限届满或达到标称 TBW 为终止条件,以先到者为准。JESD218 还规定了达到额定耐久度后的断电数据保持时间:客户端类别为 30 °C 下一年,企业类别为 40 °C 下三个月。断电存放的 SSD 并不是归档介质,请遵循 3-2-1 备份指南。

温度、降速与散热片

每个主控都会在 Identify Controller 中公布两个阈值。达到 WCTEMP 时,它仍会继续工作,但需要加强散热或降低负载;达到 CCTEMP 时,它可能大幅降速、关机甚至丢失数据。NVM Express Base Specification 2.4 以开尔文定义这些温度,并建议将警告阈值设为 343 K,约 70 °C。

PCIe 5.0 硬盘和高速 PCIe 4.0 硬盘在持续负载下通常需要散热片。只用一个散热片即可,可以是主板带导热垫的 M.2 盖板,也可以是硬盘自带的散热片,并确保有气流经过。在笔记本和小机箱里,功耗较低的硬盘往往能比会降速的高速硬盘完成更多持续工作。SMART 日志会记录达到或超过每个阈值的累计分钟数。如果警告计数在正常使用中不断增长,就需要改善散热。

掉电保护与企业级硬盘

客户端硬盘在数据进入易失性缓冲区后就会确认写入完成,并依赖主机在断电前发出 flush 命令把数据刷写下去。SMART 日志在 Unexpected Power Losses 字段中统计突然断电次数,该字段在早期规范版本中名为 Unsafe Shutdowns;规范也警告,没有掉电保护的硬盘可能发生数据损坏。

带掉电保护(PLP)的企业级硬盘配有电容,在断电后仍有足够能量把缓冲区中的数据和元数据写入 NAND。已确认的写入不会丢失,flush 也无需等待 NAND 就能完成。因此,频繁调用 fsync 的软件,例如数据库、预写日志、ZFS intent log 设备和 Ceph,会运行得更快、更稳定,Ceph 也推荐使用带 PLP 的企业级硬盘。

nvme id-ctrl /dev/nvme0 | grep -E '^vwc '
cat /sys/block/nvme0n1/queue/write_cache

vwc 字段表示主控是否报告易失性写缓存,write_cache 中的 write back 表示 Linux 会向硬盘发送 flush 命令。是否具备 PLP,以规格书为准。永远不要为了跑分好看而在客户端硬盘上关闭 flush 或文件系统写屏障。

用 fio 测试新硬盘

请在硬盘存放数据之前进行测试,并使用其文件系统上的文件,因为直接写入 /dev/nvme0n1 这类裸设备会破坏其中的内容。以下任务使用直接 I/O 和 io_uring;在较旧的内核上请改用 --ioengine=libaio。fio 会先生成测试文件,因此读取命中的是真实写入过的数据。

cd /mnt/newdrive

# 队列深度 1 的 4K 随机读取:台式机真正感受到的指标
fio --name=qd1-randread --filename=fio.test --size=32G \
  --ioengine=io_uring --direct=1 --rw=randread --bs=4k \
  --iodepth=1 --time_based --runtime=60 --ramp_time=5 \
  --lat_percentiles=1 --percentile_list=50:99:99.9

# 带并行的混合 4K 随机读写:更接近繁忙的服务器
fio --name=qd32-randrw --filename=fio.test --size=32G \
  --ioengine=io_uring --direct=1 --rw=randrw --rwmixread=70 --bs=4k \
  --iodepth=32 --numjobs=4 --group_reporting --time_based --runtime=120

# 超出 SLC 缓存的顺序写入,每秒记录一次带宽
fio --name=seqwrite-cliff --filename=fio-big.test --size=300G \
  --ioengine=io_uring --direct=1 --rw=write --bs=1M --iodepth=8 \
  --write_bw_log=cliff --log_avg_msec=1000

对于 QD1 任务,不仅要看 IOPS,还要看平均延迟和 p99。对于混合任务,比较不同硬盘的 p99.9 延迟。断崖测试的数据量应大于预期缓存、小于剩余空间。该任务会生成 cliff_bw.1.log,每秒一行,时间以毫秒计,带宽以 KiB/s 计;画成曲线后就能看到缓存内速度、断崖位置以及可能出现的降速。测试结束后删除测试文件。fio 文档 说明了每个选项。

用 smartctl 和 nvme-cli 读取健康状态

smartmontools 和 nvme-cli 读取的是同一份 SMART / Health Information 日志:

smartctl -x /dev/nvme0
nvme smart-log /dev/nvme0

2026 年 9 月发布的 nvme-cli 3.0 把命令改成了 nvme log smart、nvme id ctrl 这样的形式。本文使用的带连字符的旧命令名仍作为已弃用的别名保留,在 nvme-cli 2.x 中同样可用。

  • Critical Warning:任何非零位都需要处理。这些位涵盖备用块余量、温度、可靠性,以及介质是否进入只读状态。
  • Available Spare:剩余备用容量的百分比。如果它逐渐逼近 Available Spare Threshold,说明硬盘正在淘汰坏块。
  • Percentage Used:厂商对已消耗耐久度的估算。数值可以超过 100,但这本身并不代表故障。
  • Data Units Written:以千个 512 字节单位计数。乘以 512,000 即得字节数,因此 10,000,000 个单位等于 5.12 TB。每月记录一次,就能算出自己实际的 DWPD。
  • Media and Data Integrity Errors:无法恢复的错误。只要有增长,就应立即核查备份。

自检与固件

smartctl -t short /dev/nvme0
smartctl -l selftest /dev/nvme0
nvme fw-log /dev/nvme0
fwupdmgr get-updates
fwupdmgr update

设备自检在 NVMe 规范中是可选功能,因此有些硬盘不支持。请让 smartd 或监控系统针对上述字段发出告警。固件会影响性能、电源管理和错误处理,因此更新前请阅读发布说明并做好备份。fwupd 的 NVMe 插件 可以安装在 LVFS 上发布更新的厂商的固件,并在下次重启时激活。其他厂商则需要使用各自的工具,或用 nvme fw-download 和 nvme fw-commit 刷入厂商提供的镜像。对于镜像阵列,先更新其中一块硬盘。如果硬盘在 Linux 下从总线上掉线,先查找是否有修复该问题的固件;内核参数 nvme_core.default_ps_max_latency_us=0 可以关闭自主电源状态切换,作为诊断手段。

Linux 下的分区对齐与 TRIM

fdisk 和 parted 会按照设备报告的 I/O 拓扑对齐分区,因此保留默认值并检查结果即可:

lsblk -t /dev/nvme0n1
parted /dev/nvme0n1 align-check optimal 1
nvme id-ns -H /dev/nvme0n1 | grep 'LBA Format'

parted 的 align-check 命令 会报告分区 1 是否达到最佳对齐。最后一条命令列出该命名空间支持的 LBA 格式,并标出当前使用的格式。许多硬盘出厂时使用 512 字节扇区,同时也支持 4096 字节扇区。nvme format --lbaf=<n> 可以切换格式,但会清空整个命名空间,所以要在存放数据之前做决定,并先确认引导程序、RAID 层和虚拟化平台都支持 4Kn 设备。

TRIM 会告诉硬盘哪些块已经空闲,这样垃圾回收就不必搬运过期页面,写入放大随之降低,缓存外写入速度也更稳定。建议使用定期 TRIM:

sudo systemctl enable --now fstrim.timer
sudo fstrim -av
lsblk --discard

fstrim 手册 认为,对大多数台式机和服务器而言每周 TRIM 一次就足够了,并提醒频繁 TRIM 或使用 discard 挂载选项可能缩短劣质 SSD 的寿命。从内核 6.2 起,Btrfs 会在支持的设备上自动启用 discard=async。lsblk --discard 输出中 DISC-GRAN 和 DISC-MAX 不为零,说明 discard 请求能够到达设备。dm-crypt 默认忽略 discard 请求,因为它们可能暴露哪些块正在使用。只有在可以接受这种信息泄露时,才通过 crypttab 的 discard 选项或 --allow-discards 启用它。同时要保留剩余空间,因为 SLC 缓存和垃圾回收都依赖空闲块。

按用途选择硬盘

需求 游戏 PC 开发或创作工作站 数据库或虚拟化服务器 以读取为主的大容量存储
接口 PCIe 4.0,5.0 可选 PCIe 4.0,或 CPU 插槽上的 5.0 U.2、E1.S、E3.S 或 M.2 22110 PCIe 4.0 或 3.0,芯片组插槽即可
QD1 下的 4K 随机性能 高优先级 高优先级 高,另需高队列深度 IOPS 和 p99.9 低优先级
DRAM 或 HMB HMB 可接受 优先 DRAM DRAM HMB 可接受
NAND TLC,游戏库可用 QLC TLC 企业级 TLC QLC 可接受
缓存外写入 低优先级 高优先级 看稳态指标 低,除非导入量大
耐久度 0.3 DWPD 足够 按实际写入量计算 1 DWPD 或以上 低优先级
掉电保护 不需要 本地数据库有用 必需 不需要
散热 主板散热片 散热片加机箱风道 服务器机箱风道 主板散热片

购买前后的检查清单

  1. 描述负载:每日写入量、单次最大写入量、访问模式,以及是否运行频繁调用 fsync 的软件。
  2. 查阅主板说明书,确认哪些 M.2 插槽直连 CPU,以及它们会禁用哪些接口。
  3. 随机负载重或写入量大的工作选择带 DRAM 的 TLC,HMB 或 QLC 留给以读取为主的用途。
  4. 根据 TBW 计算 DWPD,并阅读质保条款。
  5. 服务器应要求具备 PLP、DWPD 指标和明确的固件更新途径。
  6. 只装一个散热片并保证有气流经过,一周后检查温度计数。
  7. 安装后确认链路速度和宽度,并记录固件版本。
  8. 运行三个 fio 任务,把结果保存为基准。
  9. 启用 fstrim.timer,检查对齐,并在存放数据前确定 LBA 格式。
  10. 监控 Critical Warning、介质错误、Available Spare 和 Percentage Used,并把备份保存在其他介质上。

更多文章