我们知道机械硬盘最大的缺点在于,寻道时间比较长,也就是不适合随机小块IO。所以这几年固态存储大行其道,因为它对任何地址访问的开销都相等,节省了机械寻道时间,所以随机IO性能很好。
当前的SSD硬盘,单块SSD的大块连续读吞吐量超过了350MB/s,写超过了210MB/s,甚至4K块随机读吞吐量超过了200MB/s,写超过了180MB/s,随机读IOPS超过了600,随机IO延迟不超过1ms。这种速度满足当前主流的架构不成问题。
SSD固态硬盘的硬件组成
SSD(Solid State Drive):是一种利用Flash芯片或者DRAM芯片作为数据永久存储的硬盘。所以不再使用磁技术来存储数据。
利用DRAM作为永久存储介质的SSD,又称为RAM-Disk,使用DRAM内存条来存储数据,所以在外部电池断开后,需要使用电池来维持DRAM中的数据。
- 基于Flash介质的SSD:使用“浮动门场效应晶体管”的晶体管来保存数据,每个这样的晶体管叫做一个“Cell”,即单元。有两种类型的单元:
Single Level Cell(SLC):每个单元可以保存1B的数据
Mult Level Cell(MLC):每个单元可以保存2B的数据。所以MLC容量是SLC的两倍,成本却与SLC大致相当。但因为MLC的每个单元需要存储2B,所以复杂度比较高,出错率自然比较高。
Flash 芯片
Cell
本节我们介绍一下Cell的构成,这就是SSD可以在掉电后不丢失信息的原因
SSD是由浮动门构成的,所谓浮动门是一个逻辑电路,四周被$SiO_2$包裹着。在Word Line(字线)上抬高电势,电子存储在浮动门中,字线恢复电势,电子不会丢失,也就是说浮动门仍然是充电的。
那么什么时候状态为0,什么时候状态为1呢?充电到一定电势的后表示0,电势降到一定的阈值以后,表示为1。
在前面我们说过,MLC可以在一个Cell里面保存多位,也就是一个Cell可以有多种电势阈值,这样就可以表示00,01,11,10 4种状态。
Cell串
把多个Cell串联起来,就组成了Cell串。对每个cell串,每次只能读写其中一个Cell,所以我们会把多个Cell串并联起来,并行操作,这样就可以读多位数据呢。
NADD就是由这种晶体管有序排列起来的Flash芯片。每4314*8=34512个Cell组成一个Page,当然这只是逻辑上,它是IO的最小单位。每个Page可以存放4KB的内容。
每128个Page组成一个Block,每2048个Block组成一个Plane区域。而一个Flash芯片由两个区域组称,一个存储奇数序号,另一个存储偶数序号。
SSD固态盘的构成
上面我们讲解了NAND Flash芯片的主要构成部分,现在我们来看SSD固态盘的整体构成。
下图为Intel X-25M固态硬盘的拆机图。里面包含:
- 10片NAND Flash芯片
- SSD控制器
- RAM Buffer
SSD控制器芯片负责向所有的NAND Flash芯片执行读写任务,同样也就通过指令的方式来运作,因为地址信息和数据信息都在这8位的总线上传送,加上总线位宽太窄,所以一个简单的寻址都需要多个时钟周期才能仅完成。
我们知道芯片容量越大,地址就会越长,寻址时间也就会越长,对小块随机IO,Flash会随着容量的增加而变得越来越低效。
读写数据流程
下面来看一下读写操作都需要做哪些工作?
如何读数据
当需要读某个Page时,Flash控制器将这个Page的字线组电势置为0,可以将电势值解码成1或者0,放到SSD的RAM Buffer中。
从上述过程就可以看出SSD的最小IO单位为1个Page
如何写数据
Flash芯片要求在修改一个Cell中的位之前,必须擦除这个Cell。其实这个擦除动作就是将一个Block一下全放电。也就是每次擦除只能一下擦除整个Block,将所有的Cell全置1。
这就是SSD的一个非常致命的缺点,它不能单独擦除某个Page或者Cell
擦除完毕以后,SSD会以Page为单位进行写入。
SSD的问题
SSD天然的缺陷
Flash芯片在写入数据的时候有很多效率低下的地方,这是Flash芯片的通病
擦除前需要将整个Block清掉(Erase before overwrite)
上面我们讲到过如果要写入数据,必须先Erase整个Block,而不只是把一个Page或者Cell给清掉。相比于机械磁盘,多了擦除的这个步骤。
特别是如果仅仅需要更改某个Page的内容,却需要擦除整个Block,性能浪费很严重。我们可以看看修改只修改某个Page的过程,
在擦除之前,先将Block的数据读入RAM中,
然后擦除整个Block,
再更新RAM中的对应Page,写回Flash芯片中
这也是SSD的缓存通常比较大的原因。
这就叫做写扩大,我们姑且称之为写惩罚
这个时候我们会问, 为什么如果只需要修改一个Cell,却需要把Block放电?
因为Cell之间是存在干扰的,如果有的在充电,有的在放电,则会产生不可控的干扰问题。所以不如直接将所有的先放电。
那 为什么需要一次擦一个Block,而不是一个Page?
这是管理粒度的问题,粒度越小,管理开销越大,所以一次擦一个Block是一种比较折中的方案。
不过幸好,向Free Space写入数据时,因为里面没有被写过,所以不需要擦除,当然没有写惩罚。
但是另一个问题又来了,SSD如何知道哪些是Free Space?
我们知道只有文件系统才知道硬盘上哪些数据是有用的,它会使用元数据来进行记录,即使是删除一份数据也只是修改了元数据,而不是把数据真正的删除掉。
这样看来,SSD其实是不知道硬盘上哪些地方是所谓的Free Space,它只会把数据往从来没修过的地方写,所以Free Space会越来越少。
- Wear Off:
什么是Wear off?就是逻辑门充放电次数过多,$SiO_2$绝缘层的绝缘能力遭到损耗,逐渐不再绝缘,无法保证有充足的电荷,也就是Cell已经物理损坏了。
更糟糕的是,损坏的Cell会拖累整个Page,因为最小的寻址单位是Page。这个Page的逻辑地址会被重定向到其他的Page上
MLC因为器件复杂,可擦写的寿命小于10000次,而SLC则十倍于MLC,小于100000次。
如何解决?
之前我们提到SSD在写上有会极大的写惩罚,而且会加速Wear off,有什么好的解决方案?
方法一:拆东墙补西墙重定向写
如果同一个Cell被高频擦写,那么它被损坏的几率当然增大。如果一个Page之前都被写过了,我们不如把所有针对这个Page的写请求重定向到Free Space上.
这样的好处在于对Free Space的写是不需要提前擦除的,减少了擦除次数。不过这些被写过的Page也不能浪费了,我们可以把他们标为"Garbage",等待比较多的时候,再批量回收。
这样做的目的是将写操作平衡到所有可能的Block中,降低单位时间内的每个Block擦写次数。问题是,Free Space会越来越少,重定向写的几率也会越来越少,最后降为0.
而且因为有重定向,SSD内部很定需要维护一个地址映射表,需要SSD的CPU能维护一定比较复杂的程序,称为
wear Leveling
(损耗平衡算法)方法二:定期清垃圾
前面我们说过SSD自己是不知道哪些空间是Free Space,但是文件系统知道,可以在操作系统里面运行一种Wiper,可以不断扫描,然后把空闲空间通知给SSD,由SSD来执行擦除工作。
不过这种清除工作只能定期执行
方法三:持续清除体内垃圾
有没有办法可以让文件系统在删除之后实时通知SSD。
可以使用ATA指令里面一个功能——TRIM,现在已经集成在很多SSD的Firmware中了。
方法四:IO 优化
之前的方法主要着力点在Free Space。我们也可以采用另一种思路,对IO进行优化。
比如Delay write。现在有两个针对同一个地址的写IO,在Write1还没写到硬盘之前,Write 2就到了,控制器直接用Write 2来覆盖Write 1 ,这个操作是在内存里面的,省去了Write 1 写入硬盘的过程。这种机制为“写命中”的一种情况。问题就是数据可能不一致。比如有如下IO:Write 1 , Read 2 , Write 3 ,此时如果用Write 3取代Write 1,那么Read 2 读出了Write 3 的内容,实际上Read 2应该读Write 1的内容,所以数据不一致。
另外还可以使用Combine Write,对机械硬盘来说,如果控制器一段时间内收到多个写IO,而这些写IO的地址在逻辑上是连续的,可以将小写IO合并为大IO,一次性写入,节约了SCSI指令周期。
对SSD来说,逻辑地址和物理地址存在一个映射关系,我们读数据的时候是根据这个映射关系来的,所以可以任何地址的小IO整合为大IO,直接写到Free的Block中。因为SSD需要对数据进行合并以及优化,所以SSD对收到的写数据一般采用Write Back模式,即收到主机控制器的数据立即返回成功,然后异步处理。这样就存在一个风险,如果掉电了,数据就会丢失,所以SSD是需要掉电保护机制的,一般使用一个超级电容来维持掉电之后的脏数据刷盘。
预留备用空间
为了防止文件系统将数据写满的极端情况,SSD可以自己预留一部分备用空间用于重定向写,因为不通知文件系统,所以只有SSD才知道,这样就有了一个永远不会被占用的定额Free Space。
下面说一个题外话, 为什么不需要对SSD进行碎片整理?
我们来看一下机械硬盘为什么需要进行碎片整理?机械硬盘主要瓶颈在于寻道时间,因为碎片是不零散的块,寻道次数会很多。整合到一起呢,逻辑上连续的LBA地址同样也是物理上连续的,磁盘臂换道的时间就少了。
但是对SSD而言,没有了磁盘臂,它是通过映射关系来读的,即使整理了碎片也没啥用。反而做了大堆无用功,还会减少SSD的寿命。
SSD如何处理Cell损坏
机械硬盘如果损坏,则该扇区不能磁化,磁头会感知。
而Flash中的Cell被击穿一定次数后,损坏的几率很高,
SSD如何判断损坏的呢?实际上Cell只有充电和没充电两种状态,那么电路实际上无法直接判断是漏电导致的还是说原本就是没电的。
只好使用ECC纠错码,每次读出某个Page需要进行ECC校验。Flash厂商会在Datasheet中给出最低要求,即使用该颗粒起码配合使用何种力度的纠错码。比如[email protected],意味着512B的范围内出现8b错误,可以纠错。如果超过了,就只能上报“不可恢复错误”。
厂商给出的纠错码力度越低,说明颗粒的品质越好,损坏率越低。
SSD的前景
以上缓解SSD效率的问题都是治标不治本的,为了解决多个问题,设计了若干的补救措施,需要TRIM来维持,而且数据不能占得太满。SSD在使用的时候也略显尴尬,因为成本太高,用户若需要一个10TB的存储系统,不可能都用SSD,所以很多厂商出品了SSD + HDD混合存储,其实就是将Flash芯片作为磁盘的二级缓存,一级缓存是RAM,二级是FLASH,三级是磁盘片。
有人说传统磁盘有64MB的RAM缓存,为什么还需要Flash作为下一级的缓存?如果只用磁盘的RAM,首先空间比较小,很快就塞满了,另外不能掉电,刷到盘片里面的时候,会导致性能骤降。
而使用Flash芯片再加一级缓存,可以把RAM中的数据存储Flash中,最关键的是可以掉电,这样可以直接回复给控制器成功之后,磁盘驱动器再在后台将数据从Flash中写到磁盘片中,这样既比纯SSD便宜,还保证了性能。