@babydragon
2016-05-17T21:20:25.000000Z
字数 4197
阅读 2868
ssd
最近,SSD(solid state drives,固态硬盘)已经开始广泛应用以提高应用程序的IO性能。大量数据标明,相比于传统HDD(hard disk drives,硬盘),SSD拥有非常高的读写性能,进而提高应用程序性能。然而,通常情况下只是简单的使用SSD替换HDD,以获取更高的IOPS。如果能够将应用程序设计得SSD友好,既能进一步提升应用程序性能,也能延长SSD寿命。
在应用程序针对SSD优化之前,我们首先需要对SSD的结构和特性有所了解。
相比于普通应用程序,SSD友好的应用程序有以下优点:
虽然直接升级到SSD已经可以大大提高应用程序的qps(queries per second,每秒查询数),如果针对SSD特性进行优化之后,还可以继续提升qps。
我们有一个应用程序,之前在使用HDD时,最高的qps为142;更换成SSD之后,即使没有对应用程序进行优化,借助于SSD的高IOPS,qps也提升到了20,000,提升了超过140倍。
当应用程序针对SSD进行了优化之后,最高性能提升到了100,000qps,又提升了超过4倍。这里的优化,主要利用了SSD内部并行处理机制(下文会提到),通过多个并行线程处理IO提升应用程序IO性能。
图1,线程和应用程序吞吐率关系
前文提到过,SSD内部的IO最小单元是页(通常大小是4KB),因此即使是读写一个字节数据,SSD还是会操作整页数据。这也是写入放大的其中一个原因。如果应用程序非SSD友好,可能会大大增加写入放大因素。
SSD的寿命通常由以下因素决定:SSD大小、PE周期大小、写入放大因子和应用程序写入速率。考虑到SSD成本,如果能够优化应用程序减少写入放大因子和写入速度,能够有效延长SSD寿命。
在进行应用程序本身的优化之前,SSD友好设计可以先从文件系统、数据库、数据存储设施层面开始考虑。
文件系统直接和存储进行交互,文件系统的优化主要针对以下几个SSD特性:
SSD友好的文件系统有两类。一类是适配了SSD的通用文件系统,这些文件系统都通过支持SSD的TRIM指令来进行优化,包括Ext4、Btrfs。另一类是专门为SSD设计的文件系统,它们自己维护了日志结构以迎合SSD的“读取-擦除-写入”流程,例如NVFS、JFFS/JFFS2、F2FS。
传统数据库组件的设计,都充分考虑到了HDD特性。其中最受人关注的就是HDD的顺序读写性能远优于随机读写,因此数据库存储、查询优化等都会尽可能的利用该特性。
但是对于SSD来说,这些特性都可能不复存在,目前有两类专门针对SSD优化的数据库:
由于HDD的延迟,读取本地HDD上的数据延迟,可能会大于网络加上内存的延迟。基于这种情况,一些公司会使用例如Memcached、Redis等内存存储集群,作为数据存储或者集中式的缓存。
但是,如果使用了SSD,情况就不同了。SSD的IO延迟可以降低到微秒级别,且相比于HDD有更高的读写带宽。相比于使用内存作为存储,SSD除了性能的提升,还可以大大降低成本和软件设计的复杂度。
在应用程序层面,我们同样可以针对SSD特性进行优化,在提高应用程序性能的同时,提高SSD的使用寿命。
这些优化主要分为三类:数据结构、IO处理和多线程。
由于HDD在查找数据时又寻道时间,为了避免寻道产生的延迟,应用程序常常被优化成就地更新。图1(左)展示了HDD进行就地更新和随机更新时的qps差别,可以发现对于HDD避免寻道时间,对IO的提升还是比较大的。
图2,HDD和SSD随机更新和就地更新qps
反过来看图1右侧图,对于SSD情况却截然相反。正如前文提到的,SSD的特性决定了它无法直接写入已经有数据的块,而是需要经过“读取-擦除-写入”的流程。这个流程既降低了数据写入速度,又导致了写入放大,最终导致了如图所示的qps下降。反之,对于随机写入,SSD可以寻找一个直接可写的块并写入,避免了上述流程。
通常来说,应用程序在存储数据的时候,不会考虑数据访问和修改的频率。假设我们将冷热数据混合排布在同一个区块,对于SSD来说,如果要修改其中的一小块内容(小于1页),SSD仍然会读取整页的数据。这样同样会降低IO带宽和导致写入放大。
因此,出于性能考虑,如果应用程序将SSD作为数据存储,应该将数据按照访问和修改频率划分。将不同热度的数据存放在不同位置,以提高SSD读写性能。
SSD的读取以页为单位,再加上操作系统通常会采用预读取操作,应用程序所采用的数据结构尽可能的紧凑,能够减少SSD的读取操作,同时也能更好地利用页缓存。
同样的,由于SSD写入方式的特殊性,紧凑数据结构将关联数据放置到相邻区域,减少可能的垃圾回收的同时,还能够降低写入放大带来的问题。
前文介绍过,SSD内部有类似JVM的垃圾回收机制。SSD会收集内部可回收区域,并且设置一个空闲块的阈值。当空闲块数量低于阈值的时候,SSD会进行后台垃圾回收,以擦除可写区域。由于后台垃圾回收操作是异步的,因此它不会阻塞应用程序的IO操作。但如果此时写入IO频率高于后台垃圾回收的清理速度,SSD会启动前台垃圾回收。前台垃圾回收操作会阻塞的清理应用程序即将写入的块,此时应用程序IO必须等待待写入块被擦除完毕后才能执行后续的写入操作。此时应用程序IO的延迟可能会达到毫秒级。
下图是针对此特性进行的写入延迟测试,整个测试通过控制不同写入速率的数据写入,持续2小时,监测写入的延迟。
图3,高频数据写入导致的延迟
从左图我们可以看见,当写入速率达到800MB/s的时候,监测到的大于50ms延迟数量达到了61次。而右图可以看见,此速率下监测到的最大延迟达到了92ms。
SSD的使用空间会影响到SSD的写入放大和垃圾回收频率。在垃圾回收过程中,块中的有效数据需要被移动到空闲的块上。假设SSD使用空间为A%,平均情况下,如果需要擦除一个块,需要压缩的块会有1/(1-A%)。在实际情况下可能会更糟糕,下图展示了随着SSD使用率上升,需要被压缩的块和页的数量:
图4,SSD使用率对块和页的压缩数量
SSD有不同层面的内部并行机制:通道(channel)、包装(package)、芯片(chip)和平面(plane)。单个IO线程无法充分利用这些并行机制。SSD能够通过内部的多通道机制,将多个IO线程分配到不同的通道并行进行IO操作,以提供尽可能高的IO性能。
这里的轻量IO是有多轻呢?通常的计算方式是IO数据量不超过内部的并行机制。例如,页大小为4K,并行度(通道数)为16的SSD,阈值在64KB左右。
该原则和第6条并不相矛盾,当IO的读写数据量很大时,少量IO线程(如1到2个)已经占满了SSD的总IO带宽,此时如果继续增加IO线程数,反而会降低总的IO性能。因为过量的IO线程之间,会对SSD映射表等资源产生竞争,同时也会破坏操作系统提供的预读取等优化机制。在我们的测试示例中,当写入大小为10MB的时候,单线程可以达到414MB/s的写入速率,两个线程可以提升到816MB/s的总写入速率,但是当写入线程增加到8个时,总的写入速率却降低到了500MB/s。
图5,IO大小和线程数量对吞吐率的影响
应用程序使用SSD会比使用HDD有更好的IO性能。然而如果不进行应用程序优化,可能无法达到最优的性能。本文介绍的SSD友好的应用程序设计思路,可以帮助应用程序充分利用SSD的性能。