新盘跑分很快,用了一个月就慢下来了
选型时对几块固态盘各跑了十分钟的随机写测试,挑了数字最高的那款。上线一个月后,同样的测试命令,结果比当初低了一大截,而且波动很大。盘没有坏,健康指标一切正常。
固态盘的写入性能不是一个固定的数:它取决于盘当前的状态,而状态取决于此前写过多少、怎么写的。全新的盘和写满过的盘,是两种不同的状态。十分钟的测试量到的是前一种,生产环境长期处于后一种。
本文说明这个差别的来历,以及怎样测到能代表生产状态的数字。
本文没有配套实验。 稳态测试需要在一块可以被完全覆盖的真实固态盘上长时间写入,本站的实验环境不具备这个条件。下面的内容来自存储行业公开的测试规范、设备厂商文档和内核文档,不含本站实测的数字;图中的曲线是示意。
一、不能原地覆盖
闪存有两个与机械盘完全不同的性质:
- 写入只能写到已经擦除过的位置,不能直接覆盖旧数据。
- 写入的最小单位是页(通常几 KB 到十几 KB),擦除的最小单位是块(由几百个页组成,几 MB)。要擦除就得整块擦。
所以「修改一个页」的实际过程是:把新内容写到别处一个空闲的页,把原来的页标记为无效。盘内部维护一张从逻辑地址到物理位置的映射表,主机看到的地址不变。
无效的页越来越多,空闲的页越来越少。要重新得到空闲的页,盘的控制器必须做垃圾回收:选一个无效页比较多的块,把里面仍然有效的页搬到别处,然后把整块擦除。
二、三个阶段
全新状态。 盘是空的,到处都是擦除好的块。每次写入直接落到空闲页上,不需要任何额外的搬运。这时测到的是闪存和控制器的最高速度。
过渡。 写入的总量接近盘的容量之后,空闲的块用完了。之后每写入一点,垃圾回收就得先腾出相应的空间。前台写入和后台搬运争抢同一份资源,速度明显下降,并且出现波动。
稳态。 继续写下去,垃圾回收的工作量与写入量达到平衡,性能稳定在一个比全新状态低的水平。
稳态下,主机每写入一份数据,盘内部实际要写入不止一份(多出来的是被搬运的有效页)。这个倍数就是盘内部的写放大。它不是定值,取决于下面几个因素。
三、什么决定稳态的水平
预留空间。 盘的实际闪存容量比标称容量大,多出来的部分主机看不到,专门给垃圾回收周转。预留越多,回收时每个块里的有效页越少,搬运越少。企业级的盘预留比例普遍高于消费级的盘,这是两者在持续写入下表现不同的主要原因之一。自己也可以增加预留:分区时留一部分空间不用,前提是这部分空间从未被写过或者已经被释放。
盘用了多满。 道理相同:有效数据占得越满,可供周转的空间越少。一块用到九成满的盘,持续写入的表现比用到五成满时差。
写入的模式。 顺序写入、大块写入时,一个块里的页往往一起失效,回收时几乎不用搬运。小块的随机写入让每个块里有效页与无效页混在一起,搬运最多。所以稳态下顺序写的速度与全新状态相差不大,随机写的速度下降最明显。
释放通知。 文件系统里删除了文件,盘并不知道那些地址上的数据已经没用,垃圾回收时还会把它们当作有效数据搬来搬去。释放指令(SATA 里叫 TRIM,NVMe 里叫 Deallocate,SCSI 里叫 UNMAP)就是用来告诉盘「这些地址不用了」。Linux 上通常用定期执行的 fstrim 完成,也可以在挂载时打开实时的 discard。没有释放通知时,盘在自己看来始终是满的。
缓存策略。 很多消费级的盘把一部分闪存当作高速缓存来用,写入先进缓存,空闲时再整理到主存储区。缓存没用完时速度很高,用完之后直接降到主存储区的速度,下降的幅度可以非常大。这是另一种「短测试很快」的来源,与垃圾回收无关,但效果叠加。
上层软件的写入方式也会放大或减轻这个问题。LSM 树的压实把业务写入量放大了几倍,这些放大的写入同样要计入盘的负担和寿命,见写了 180 MB,磁盘实际写了 700 MB。
四、怎么测才算数
存储行业的测试规范(SNIA 的固态存储性能测试规范)对此有明确的步骤,思路是三步:
- 清空。 用安全擦除或全盘释放把盘恢复到一个确定的初始状态,让每次测试从同一个起点开始。
- 预处理。 先把整块盘顺序写满,通常写两遍,让每个逻辑地址都有数据;再用待测的负载持续写入。
- 测到稳定为止。 分轮次记录指标,直到最近几轮的结果变化落在规定的范围内,才取这个窗口里的值作为结果。
自己做选型测试时至少要做到:
- 写入的总量是盘容量的数倍,而不是按时间跑十分钟。
- 看整条曲线,而不是只看平均值:记录每秒的吞吐与延迟,观察什么时候下降、降到多少、波动多大。
- 关注延迟的高分位。稳态下平均吞吐可能还过得去,垃圾回收造成的偶发长延迟才是影响业务的部分。
- 测试负载接近真实负载:块大小、随机与顺序的比例、读写混合比例、并发数、盘的填充程度。
- 在与生产相同的软件栈上测。文件系统、是否启用释放通知、上层数据库的写入模式都会改变结果。
这类测试会覆盖盘上的全部数据,并且消耗盘的写入寿命。只能在专门用于测试、没有任何数据的盘上进行,不能在有数据的盘或生产环境的盘上做。
云上的块存储另有一层:性能受购买的规格限制,有的类型带突发额度,额度用完之后降到基线。短测试量到的是突发性能,长时间运行得到的是基线性能。现象与本文类似,原因在计费模型而不在闪存。
五、与 iowait 的关系
盘变慢之后,系统监控上常见的表现是 CPU 的 iowait 升高。iowait 表示的是「CPU 空闲,同时有任务在等待 I/O 完成」的时间占比,它有两个容易误读的地方:
- 它高不代表 CPU 忙。恰恰相反,这段时间 CPU 是闲着的。
- 它低不代表 I/O 没问题。如果同时有别的任务把 CPU 用满了,等 I/O 的时间就不会被记为 iowait。
判断存储是否成为瓶颈,更直接的是看块设备自己的指标:每个请求的平均等待时间、队列长度、设备的繁忙比例(iostat -x 里的相应列),以及应用自己记录的读写延迟分位。Linux 的压力指标(/proc/pressure/io)给出的是「有任务因为等 I/O 而停顿」的时间比例,比 iowait 更接近想知道的事。
六、检查清单
- 选型数据来自预处理之后的稳态测试,能说出盘被写入了多少倍容量。
- 看过整条时间曲线和延迟的高分位。
- 生产环境启用了定期的释放通知,并确认它在所用的文件系统、卷管理、虚拟化层上真的传到了盘。
- 盘不长期用到接近全满;写入密集的场景留出额外的预留空间。
- 监控盘的写入总量与厂商给出的寿命指标,算上上层软件的写放大。
- 用块设备的延迟与队列指标判断存储瓶颈,不只看 iowait。
参考资料
- SNIA:Solid State Storage Performance Test Specification(清空、预处理与稳态判定的规范)
- Agrawal 等:Design Tradeoffs for SSD Performance(垃圾回收、预留空间与写放大)
- fstrim(8) 手册页
- fio 文档(稳态检测选项与按时间记录日志)
- Linux 内核文档:PSI - Pressure Stall Information
- iostat(1) 手册页