本文来自:码农黄师傅,作者:黄师傅,题图来自:AI生成我打算从存储系统架构师的角度谈谈如何用好SSD。本文中的“存储系统”,指的是全闪阵列,或者全闪分布式块。以前我做zStorage的时候,遇到一个问题:性能测试时间长了,写入SSD的数据量大了,写性能就下降。当时我猜测,多半是SSD后台启动了GC(垃圾收集),导致了写性能下降。没有证据,也就只能是猜测。从去年开始,我花了很多时间学习SSD,现在我可以肯定地说,就是SSD的后台GC影响了写性能。本文不打算详细讲解“为什么SSD需要做GC”,也不讲“SSD怎么做GC”,这些内容网上有很多,不了解的读者自行百度或Deepseek,推荐Deepseek。本文重点讲那些以前我不了解的、但对存储系统有影响的SSD特性。SSD的写比读性能差,随机写比顺序写差很多。现在采用PCIe Gen5 x4的NVMe SSD,主机接口带宽可以达到15.7GB/s,顺序读可以达到14.5GB/s,要去掉一些命令开销。详见下表:上表可以看出,4KB随机写的PCIe带宽利用率只能跑到15%。很明显,PCIe接口不是瓶颈,那瓶颈在什么地方?实际上,SSD的写性能瓶颈在NAND Flash上。现在企业SSD中最常用的TLC Nand Flash,一般要求一次性写入同一个WL的3个页,如果每个页8KB,那么3个页就是24KB。写入这3个8KB页需要花大约700us。也就是说,一个Flash芯片的写性能只能达到24KB / 700us = 35MB /s,如果要达到10GB/s性能,一个SSD需要配置大约300个Nand Flash芯片。看网上的SSD照片,似乎没有那么多Flash芯片?实际上,一般单个Flash芯片内部会封装很多个独立的Die(切割好的硅片),每个Die内部还有多个Plane,多个Plane可以一起执行写命令。也就是说,实际上不是300个芯片,而是300个Plane。按照每个die容量1Tb计算,15.36TB的SSD大约需要配置140个die,每个Die里面有2个Plane,顺序写性能大约就是10GB/s。为什么读性能那么高?因为读一个Flash页只需要40us。那么,又为什么随机写性能(带宽利用率14%)比顺序写性能(70%)低那么多?随机写性能低的主要原因是GC导致的写放大(WAF)很高。如果是顺序写,那么Nand Flash上数据也是被顺序覆盖掉的,一个GC回收单元里面所有Flash数据都被一起覆盖掉了,虽然还有GC流程,但是一个GC回收单元里面的所有页面都被覆盖掉了,没有有效数据页了,也就不需要拷贝数据了,所以没有写放大(WAF = nand_write_bytes / host_write_bytes = 1.0)。如果是4KB随机写,一个GC回收单元内部还有很多有效数据页,这些数据需要被复制到Nand Flash的新位置,才能删除GC回收单元。这个复制过程,会引入写放大。如果用fio做4kb随机写测试,有些型号的SSD在极限情况下写放大(WAF)可以达到4.5甚至5.0,也就是nand flash里面写入5TB数据,只有1TB是主机写入SSD盘的,另外写入的4TB数据是GC复制流程产生的。例如,上表中SSD顺序写性能10GB/s,4KB随机写性能52万IOPS,那么这个型号的SSD在4KB随机写极限情况下写放大就是WAF = 10*1E9 / (52 * 1E4 * 4096) = 4.7。GC导致的写放大不仅仅造成随机写性能低,还会影响SSD的寿命。因为导致Nand Flash芯片磨损的主要因素是擦写循环的次数,也就是往Nand Flash写入的数据量。存储系统是否有办法降低写放大?一种容易想到的办法是:把对SSD的随机写,全部改成顺序写。对这个想法,我的观点是:没必要为了减小SSD写放大这么做。如果你的系统本来就是追加写(Append-Only)的设计,那么对SSD自然就是顺序写,例如:RocksDB或者ZFS这种追加写的系统,不需要专门去做什么设计。如果存储系统是原地写(Update-In-place)的设计,改成追加写确实可以减小SSD内部的写放大,但是不能减小整个系统端到端的写放大,也就是写入Nand Flash数据量并没有减少。因为改成对SSD追加写,存储系统就要增加GC流程,也就是把写放大从SSD内部搬到了存储系统软件中。从更上层应用角度看,整体写放大并没有减少。上面做法也有另外一个好处,就是存储系统可以决定什么时候启动GC流程。如果你知道什么时候业务压力低,就在业务压力低的时候启动GC流程,这样可以把GC对性能的负面影响降低到最小。SSD内部GC流程,总是倾向于尽量晚一点启动,因为晚一点启动GC流程,就会有更多的数据页被覆盖,这样需要复制的有效页就少一点,写放大也就会小一点。在我看来,这个好处并不够大,不值得为了这个把存储系统改为追加写。另一个需要注意的问题是:如果你决定改为追加写模式,那么一定要做彻底一点,追加写数据块的长度要尽量大。一般来说,现在企业SSD内部都会在Flash芯片之间做RAID,这样一旦发生LDPC无法纠正的错误(例如die失效),就可以通过RAID来恢复数据。企业SSD的GC回收单元是Super Block,构成一个RAID组所有die上对应的Block共同构成一个Super Block,这个Super Block中有一个Block是用来存放parity的,其他Block用来存放用户数据。一个Super Block会被GC流程整体回收擦除。RAID导致了GC回收单元的尺寸相当大,例如物理Flash芯片的Block大小是64MB,如果构成一个RAID的Super Block中有300个Block,Super Block的大小64MB * 300 = 19 GB。如果存储系统改为追加写之后,存储系统使用8MB的回收单元做GC,对于19GB大小GC回收单元的SSD来说,这个上层存储系统依然是随机写,不是顺序写。SSD做GC的时候,一个19GB大小的回收单元中,会有很多8MB的空洞,也会有很多8MB的有效数据。SSD的GC依然需要做数据复制,无法起到减小SSD写放大的作用。除了顺序写可以降低WAF之外,TRIM也能起到显著降低WAF的作用。当GC选中一个Super Block进行回收的时候,如果这个Super Block中有很多数据页实际上是无效的,但是还没有被新数据覆盖,也没有TRIM命令把它删除掉,那么GC流程就必须把它当作有效数据页进行复制。如果有TRIM命令把这个页面标记为无效,GC流程就不需要复制它了,就减小了写放大。Linux的ext4文件系统的mount命令有一个参数-o discard,mount的时候加了这个参数就会给SSD下发TRIM命令。除了顺序写和TRIM之外,增加OP空间(Over Provisioning)页能减小WAF。因为OP空间大了,GC流程就可以晚一些启动,SSD就可以先处理更多的写和TRIM命令,被GC选中回收的super block中的有效页面就更少,需要复制的页面更少,WAF就会更小。还有一个能降低写放大的方法是FDP。存储系统先预测哪些数据寿命短,哪些数据寿命长,然后通过FDP把预测的数据寿命告诉SSD。SSD在写入数据的时候,把寿命相同的数据放到一个Super Block中,这样当某个Super Block被选中回收的时候,里面需要复制的有效数据页就非常少,WAF就会比较小。很多企业级SSD内部的FTL粒度是4KB的,也就是一个FTL表项指向一个4KB的Nand Flash物理地址。如果存储系统下发的写操作里面有很多小于4KB的写请求,例如512Bytes的,那么SSD就需要先把4KB数据读出来,修改512B,然后再把4KB写回去。这也会影响性能,另外也造成了另外一种写放大。但如果是512B的连续写,例如写WAL日志,这是不影响的。因为企业SSD内部有备电电容,主机写到SSD的512B数据,会被暂时放到内存中,凑齐4KB之后再写Flash。也就是说,存储系统应该尽量避免不足4KB的随机写。另外,现在市面上有些超大容量(30TB、60TB、…)QLC SSD的FTL粒度不是4KB,可能是16KB或者更大的,对它们要避免不足16KB的随机写。有些存储系统会在数据写入SSD之后,立刻读出做个校验,以确保数据被正确写入了。对于SSD来说,这个做法很不好。因为如果立刻读出Nand Flash刚写入的数据,此时Nand处于不稳定状态,误码率会比较高,这可能会影响性能,也可能导致SSD内部读电压调整算法的误动作。存储系统应该尽量避免写入数据之后立刻读。SSD是否会产生坏道?是的,不过SSD产生坏道的原因跟HDD不一样。现在的TLC/QLC Nand Flash介质的误码率是非常高的,有些甚至可以达到10E-4,也就是一个4KB数据页写入Nand Flash之后,再读出来的时候,几乎必然有几个bits是错的。这些错误就是靠LDPC算法和RAID算法来恢复的,但是SSD并不能保证数据一定能被恢复。遇到无法恢复的情况,就只能把这个LBA地址的数据标记为坏道。跟HDD的坏道不一样的是,这个坏道LBA地址再重写一遍,状态就恢复了,就不再是坏道了。我搞zStorage的时候,曾经被一个问题困扰:如果读SSD的时候遇到了坏道,是否意味着这个SSD里面还有很多坏道?是否需要对这个SSD做一次全盘扫描?是否需要把这个SSD从系统中踢掉?实际上,NVMe标准提供了一个命令LBA status,这个命令可以把这个SSD的所有坏道都返回给主机,主机只需要对这些坏道数据做恢复并重写就可以了,不需要对整个SSD做全盘扫描。因为全盘扫描相当耗费时间,并且影响性能。SSD自己也会在启动内部任务在后台做扫描,并且把无法恢复的数据块标记出来。当然,存储系统每隔一段时间做一次全盘扫描,也是有必要的,毕竟自己才是最可靠的。如果充分利用LBA status这个命令,这个全盘扫描的时间间隔可以长一点。还有一点是,工作温度对SSD的影响很大,温度升高的时候,Nand Flash的误码率会升高,这会影响SSD的性能,因为需要花更多时间来恢复数据。另外,工作温度对SSD的寿命也有负面影响,高温会加速Flash Cell的隧穿氧化层老化。我能想到的,就这么多了。本文不代表任何组织。文章内容也许有错误,欢迎批评指正。本文来自:码农黄师傅,作者:黄师傅