Coding For SSD
以下为原文
SSD 的架构
NAND 闪存单元
固态硬盘(SSD)是基于闪存的数据存储设备。每个数据位保存在由浮栅晶体管制成的闪存单元里。SSD 整个都是由电子组件制成的,没有像硬盘那样的移动或者机械的部分。
在浮栅晶体管中,使用电压来实现每个位的读写和擦除。写晶体管有两个方法:NOR 闪存和 NAND 闪存。我不会更加深入的讨论 NOR 和 NAND 闪存的不同。本文将只包含被大多数制造商采用的 NAND 闪存。更多关于 NOR 和 NAND 的不同点的信息,你可以参考 Lee Hutchinson 写的这篇文章[31]
NAND 闪存模块的一个重要特征是,他们的闪存单元是损耗性的,因此它们有一个寿命。实际上,晶体管是通过保存电子来实现保存比特信息的。在每个 P/E 循环(Program/Erase,“Program”在这表示写)中电子可能被晶体管误捕,一段是时间以后,大量电子被捕获会使得闪存单元不可用。
有限的寿命
每个单元有一个最大的 P/E 循环数量,当闪存单元被认为有缺陷后,NAND 闪存被损耗而拥有一个有限的寿命。不同类型的 NAND 闪存有不同的寿命[31]。
最近的研究表示,通过给 NAND 一个相当高的温度,被捕获的电子可以被清除[14, 51]。尽管这仍然还只是研究,并且还没有确定到底哪一天能够将这个研究应用的消费市场,但这确实可以极大地增加 SSD 的寿命。
目前业界中的闪存单元类型有:
- 单层单元(SLC),这种的晶体管只能存储一个比特但寿命很长
- 多层单元(MLC),这种的晶体管可以存储 3 个比特,但是会导致增加延迟时间和相对于 SLC 减少寿命
- 三层单元(TLC),这种的晶体管可以保存 3 个比特,但是会有更高的延迟时间和更短的寿命
闪存单元类型
固态硬盘(SSD)是基于闪存的数据存储设备。比特存储在闪存单元中,有三种闪存单元类型:每个单元 1 比特(单层单元,SLC),每个单元 2 比特(多层单元,MLC),和每单元 3 比特(三层单元,TLC)。
下方的表 1 中是每种 NAND 类型的详细信息。为了比较,我添加了硬盘、内存、和 L1/L2 缓存的平均延迟。
SLC | MLC | TLC | HDD | RAM | L1 cache | L2 cache | |
---|---|---|---|---|---|---|---|
P/E cycles | 100k | 10k | 5k | * | * | * | * |
Bits per cell | 1 | 2 | 3 | * | * | * | * |
Seek latency (μs) | * | * | * | 9000 | * | * | * |
Read latency (μs) | 25 | 50 | 100 | 2000-7000 | 0.04-0.1 | 0.001 | 0.004 |
Write latency (μs) | 250 | 900 | 1500 | 2000-7000 | 0.04-0.1 | 0.001 | 0.004 |
Erase latency (μs) | 1500 | 3000 | 5000 | * | * | * | * |
Notes | * metric is not applicable for that type of memory | ||||||
Sources | P/E cycles [20] SLC/MLC latencies [1] TLC latencies [23] Hard disk drive latencies [18, 19, 25] RAM latencies [30, 52] L1 and L2 cache latencies [52] |
表1:NAND 闪存不同类型的特征和用时与其他记忆组件比较
在相同数量的晶体管中的比特数更多可以降低生产成本。与基于 MLC 的 SSD 相比,基于 SLC 的 SSD 更可靠,并具有更长的寿命,但是有更高的生产成本。因此一般的大众 SSD 是基于 MLC 或者 TLC 的,只有专业的 SSD 是基于 SLC 的。因此往往会基于硬盘的目标工作负载和可能的数据更新频率,选择正确的存储类型。对于较高的更新工作负载,SLC 是最后的选择,而高读取低写入的工作负载(例如视频存储和直播),TLC 将极其适合。另外,TLC 硬盘基于实际工作负载的基准检测显示出在实际中不必考虑基于 TLC 的 SSD 寿命。
NAND 闪存的页和块
闪存的模块组织在被称为块的格子中,而块则组织成平面。块中可以读写的最小单元称为页。页不能独立擦除,只能整块擦除。NAND 闪存的页大小可能是不一样的,大多数硬盘的页大小是 2 KB, 4 KB, 8 KB 或 16 KB。大多数 SSD 的块有 128 或 256 页,这即表示块的大小也可能是 256KB 和 4MB 之间不同的值。例如 Samsung SSD 840 EVO 的块大小是 2048KB,而每个块有 256 个 8KB 的页。页和块访问的细节在 3.1 节中
SSD 的组织
下方的图 1 是 SSD 硬盘及其组件的示例。我只是重复了数篇论文[2, 3, 6]中已有的基本示意图。
来自用户的命令是通过主机接口交换的。在我写这篇文章的时候,最新发布的 SSD 有两种最普遍的接口:SATA 和 PCIe。SSD 控制器中的处理器接收这些命令并将它们传递给闪存控制器。SSD 同样内嵌有 RAM 存储器,通常是作为缓存和存储映射信息使用。章节 4 包含了关于映射策略的细节信息。NAND 闪存芯片通过多个通道组织在一起,和这些通道有关的信息在章节 6 中。
下方的图展示出真的 SSD 是长的什么样子的。
下图是三星固态硬盘主要组件:
- 1 个 SATA3.0 接口
- 1 个 SSD 控制器(Samsung MDX S4LN021X01-8030)
- 1 个 RAM 模块(256 MB DDR2 Samsung K4P4G324EB-FGC2)
- 8 个 MLC NAND 闪存模块,每个模块有 64G 的存储空间(Samsung K9PHGY8U7A-CCK0)
下图是一个美光 P420m 企业级 PCIe 固态硬盘,2013 年末发布。主要组件有:
- PCIe 2.0 接口 x8
- 1 个 SSD 控制器
- 1 个 RAM 模块(DRAM DDR3)
- 32 通道上的 64 个 MLC NAND 闪存模块,每个模块有 32GB 的存储空间(Micron 31C12NQ314 25nm)
总存储空间为 2048GB,但在应用 over-provisioning 技术后只有 1.4TB 可用。
生产过程
很多 SSD 的生产商使用表面贴装技术(SMT,电子组件直接放置在 PCB 板上的一种生产方法)来生产 SSD。SMT 生产线由一系列机器组成,每个机器上下衔接,并有各自生产过程中的任务,例如安放组件或者融化焊锡。整个生产过程中同样贯穿了多重质量检测。Steve Burke 的两篇参观金士顿在加利福利亚芳泉谷市的生产工厂的文章[67, 68]和 Cameron Wilmot 的一篇关于台湾金士顿组装工厂的文章[69]中,有 SMT 生产线的照片和视频。
另外还有两个有趣的视频,一个是关于美光 Crucial SSD[70]的,而另一个是关于金士顿[71]。后边一个视频是 Steve Burke 文章的一部分,我同样在下方引用了。金士顿的 Mark Tekunoff 领读者参观了他们 SMT 生产线。有意思的是,视频中的每个人都穿着一套萌萌的抗静电服,看起来很有意思。
基准和性能度量
基本基准测试
下边的表 2 展示的是不同的固态硬盘在顺序和随机工作负载下的读写速度。为了便于比较,这里包含了 2008 年到 2013 年发布的 SSD、一个硬盘盒、和一个内存芯片
Samsung 64 GB | Intel X25-M | Samsung 840 EVO | Micron P420m | HDD | RAM | |
---|---|---|---|---|---|---|
Brand/Model | Samsung (MCCDE64G5MPP-OVA) | Intel X25-M (SSDSA2MH080G1GC) | Samsung (SSD 840 EVO mSATA) | Micron P420m | Western Digital Black 7200 rpm | Corsair Vengeance DDR3 |
Memory cell type | MLC | MLC | TLC | MLC | * | * |
Release year | 2008 | 2008 | 2013 | 2013 | 2013 | 2012 |
Interface | SATA 2.0 | SATA 2.0 | SATA 3.0 | PCIe 2.0 | SATA 3.0 | * |
Total capacity | 64 GB | 80 GB | 1 TB | 1.4 TB | 4 TB | 4 x 4 GB |
Pages per block | 128 | 128 | 256 | 512 | * | * |
Page size | 4 KB | 4 KB | 8 KB | 16 KB | * | * |
Block size | 512 KB | 512 KB | 2048 KB | 8196 KB | * | * |
Sequential reads (MB/s) | 100 | 254 | 540 | 3300 | 185 | 7233 |
Sequential writes (MB/s) | 92 | 78 | 520 | 630 | 185 | 5872 |
4KB random reads (MB/s) | 17 | 23.6 | 383 | 2292 | 0.54 | 5319 ** |
4KB random writes (MB/s) | 5.5 | 11.2 | 352 | 390 | 0.85 | 5729 ** |
4KB Random reads (KIOPS) | 4 | 6 | 98 | 587 | 0.14 | 105 |
4KB Random writes (KIOPS) | 1.5 | 2.8 | 90 | 100 | 0.22 | 102 |
Notes | * metric is not applicable for that storage solution ** measured with 2 MB chunks, not 4 KB | |||||
Metrics | MB/s: Megabytes per Second KIOPS: Kilo IOPS, i.e 1000 Input/Output Operations Per Second | |||||
Sources | Samsung 64 GB [21] Intel X25-M [2, 28] Samsung SSD 840 EVO [22] Micron P420M [27] Western Digital Black 4 TB [25] Corsair Vengeance DDR3 RAM [30] |
影响性能的一个重要因素是接口。最新发布的 SSD 最常使用的接口是 SATA3.0 和 PCI Express 3.0。使用 SATA3.0 接口时,数据传输速度可以达到 6 Gbit/s,而在实际上大概能够达到 550MB/s。而使用 PCIe 3.0 可以达到每条 8 GT/s,而实际上能达到大概 1 GB/s(GT/s 是指 G 次传输(Gigatransfers)每秒)。使用 PCIe 3.0 接口的 SSD 都会使用不止一条通道。使用 4 条通道的话(译注:PCIe 3.0 x4),PCIe 3.0 可以提供最大 4 GB/s,的带宽,相当于 SATA3.0 的四倍一些企业级的 SSD 同样提供串行 SCSI 接口(SAS),最新版本的 SAS 可以提供最高 12 GBit/s 的速度,但是现在 SAS 的市场占有量很小。
大部分近期的的 SSD 的内部速度可以满足 550 MB/s 的 SATA3.0 限制,因此接口是其速度瓶颈。使用 PCI Express 3.0 或者 SAS 的 SSD 可以提供巨大的性能提升。
PCI Express 和 SAS 比 SATA 要快
生产商提供的两个主要接口是 SATA3.0(550MB/s)和 PCI Express 3.0 (每通道 1 GB/s, 使用多个通道)。串行 SCSI(SAS)同样应用在企业级 SSD 上。最新版本的接口定义中 PCI Express 和 SAS 比 SATA 要快,但是同样要更贵。
预处理
如果你折磨数据足够久,它会招的 —— Ronald Coase
SSD 生产商提供的数据资料充斥着令人惊讶的性能值。确实,通过各种乱七八糟的方法对数据处理足够长的时间,生产商似乎总能找到方法在商业传单上显摆那些闪亮的数字。这些数字是否真的有意义,或者能否真的反映产品系统的性能则是另外的问题。Marc Bevand 在他关于常见 SSD 性能基准缺陷的文章中[66],提到了一些例子。例如常见的报道随机写负载的 IOPS(每秒读写操作次数)而不提所跨的 LBA(逻辑区块地址)的范围,很多 IOPS 的数据只是基于队列深度为 1,而非整个硬盘最大值而测试的。同样也有很多基准性能测试工具误用的例子。
正确评估 SSD 的性能并非易事。硬件评测博客上的很多文章都是在硬盘上随机写十分钟,便声称硬盘可以进行测试,并且测试结果是可信的。然而 SSD 性能只会在足够长时间的随机写工作负载下才会有性能降低,而所需的时间基于 SSD 的总大小会花费 30 分钟到 3 小时不等。这即是更多认真的基准性能测试开始于相当长时间的随机写负载(称为“预处理”)的原因[50]。下方的图 7 来自 StorageReview.com 上的一篇文章[26],显示出在多个 SSD 上预处理的效果。可以看见在 30 分钟左右出现了明显的性能下降,所有硬盘都出现读写速度下降和延迟上升。之后的四个小时中,硬盘性能缓慢降低到一个最小的常量值。
5.2 节解释了图 7 中实际上发生的事情,随机写入的量太大并以这种持续的方式进行使得垃圾回收进程不能维持在后台。因为必须在写命令到达时擦除块,因此垃圾回收进程必须和来自主机的工作在前台的操作竞争。使用预处理的人声称基准测试可以代表硬盘在最坏的情况下的表现。这种方法在所有工作负载下是否都是好模型还是值得商榷。
为了比较不同制造商的各种产品,找到可以比较的共同点是必要的,而最坏的情况是一个有效的共同点。然而选择在最糟糕的工作负载下表现最好的硬盘并不能保证其在生产环境下所有的工作负载下都表现的最好。实际上大部分的生产环境下,SSD 硬盘只会在唯一的一个系统下工作。因其内部特征,这个系统有一个特定的工作负载。因此比较不同硬盘的更好更精确的方法是在这些硬盘上运行完全相同的工作负载,然后比较他们表现的性能。 这就是为何,即使使用持续的随机写工作负载的预处理可以公平的比较不同 SSD,但还是有一点需要注意,如果可以的话,运行一个内部的基于目标工作负载的基准测试。
内部基准测试同样可以通过避免使用“最好的”SSD 来避免过度调配资源,譬如当一个比较便宜的 SSD 型号已经足够并且能够省下一大笔钱的时候。
基准测试很难
测试者是人,因此并不是所有的基准测试都是没有错的。在使用生产商或者第三方的基准测试的时候请小心,并且在相信这些数据之前参考多个消息源,孤证不立。如果可以的话,使用你系统的特定工作负载来进行你自己的内部基准测试。
工作负载和指标
性能基准都有相同的指标,并使用相同的度量。在本节中,对于如何解释这些指标,我希望能够给出一些见解。
通常使用的参数如下:
- 工作负载类型:可以是基于用户控制数据的指定性能基准,或者只是顺序或者随机访问的性能基准(例:仅随机写)
- 读写百分比(例:30%读 70%写)
- 队列长度:在硬盘上运行命令的并发执行线程的数量
- 访问的数据块大小(4 KB 8 KB 等)
基准测试的结果可能使用不同的度量指标。常用的如下:
- 吞吐量:数据传输的速度,通常单位是 KB/s 或 MB/s,表示千字节每秒和百万字节每秒。这个指标常用在顺序读写基准测试中。
- IOPS:每秒读写操作的数量,每个操作都是相同大小的数据块(通常是 4KB/S)。这个指标通常用在随机读写基准测试中。[17]
- 延迟:在发送完命令后设备的反应时间,通常是 μs 或 ms,表示微秒或者毫秒。
虽然吞吐量这个指标很容易理解和接受,但 IOPS 却比较难以领会。例如,如果一个硬盘在随机写上的表现是在 4KB 的数据块上是 1000 IOPS,这即表示吞吐量是 1000 x 4096 = 4 MB/s.。因此,IOPS 高只有在数据块足够大的时候才可以被解释成吞吐量高,而 IOPS 高但平均数据块小的话只能代表一个低吞吐量。
为了阐明这个观点,不妨想象我们有一个登陆服务器,每分钟要对数千个不同的文件执行微量的更新,表现出 10k IOPS 的性能。因为这些更新分布在如此多的文件里,吞吐量能够接近 20 MB/s,然而在同一个服务器中仅对同一个文件进行顺序写入能够将吞吐量提高到 200 MB/s,这可是 10 倍的提升。例子中的数字是我编的,不过这些数据很接近我接触到的生产环境。
另一个需要掌握的概念是,高吞吐量并不足以表示这是一个快的系统。实际上,如果延迟很高,不管吞吐量有多么好,整个系统还是会慢。让我们拿一个假象的单线程进程为例,这个进程需要连接 25 个数据库,每个数据库都有 20ms 的延迟。因为连接的延迟是累积的,获取 25 个连接需要 5 x 20 ms = 500 ms。因此,即便运行数据库查询的机器有很快的网卡,就当 5 GBits/s 的带宽吧,但这个脚本仍然因为延迟而会很慢。
本节的重点在于,着眼于全部的指标是很重要的,这些指标会显示出系统的不同特征,并且可以在瓶颈出现时识别出来。当在看 SSD 的基准测试并确定所选择的型号时,通常有一个很好的经验就是,别忘了这些待选的 SSD 中,哪个指标对于系统是最关键的。 对于这个主题有一个很有意思的扩展阅读:Jeremiah Peschka 写的一篇文章“IOPS 是骗局”[46]。如果你时间很紧,你同样可以直接去总结了所有其他部分的内容的第六部分。
基本操作
读出、写入、擦除
因为 NAND 闪存单元的组织结构限制,单独读写一个闪存单元是不可能的。存储单元被组织起来并有着十分特别的属性。要知道这些属性对于为固态硬盘优化数据结构的过程和理解其行为来说是很重要的。我 在下方描述了关于读写擦除操作的 SSD 的基本属性
读是以页大小对齐的
一次读取少于一页的内容是不可能的。操作系统当然可以只请求一字节,但是 SSD 会访问整个页,强制读取远超所需的数据。
写是以页大小对齐的
将数据写入 SSD 的时候,写入的增量也是页大小。因此即使一个写入操作只影响到一个字节,无论如何整个页都会写入。写入比所需更多的数据的行为被称为写入放大,其概念在 3.3 节。另外,向某页写入的行为有时候被称为“编置(to program)”一页,因此在大多数关于 SSD 的出版物和文章中“write 写”和“program 编置”是可以互相替换的
页不能被复写
NAND 闪存页只有在其“空闲”着的时候才能写入。当数据改变后,这页的内容被拷贝到一个内部寄存器,此时数据更新而新版本的数据存储在一个“空闲”的页中,这被称为“读-改-写”操作。数据并非就地更新,因为“空闲”页与原来存储数据的页不是同一个页。一旦数据被硬盘保存,原先的页被标记为“stale(意为 腐败的 不新鲜的)”,直到其被擦除。
擦除以块对齐
页不能被复写,而一旦其成为 stale,让其重新空闲下来的唯一方法是擦除他们。但是对单个页进行擦除是不可能的,只能一次擦除整个块。在用户看来,访问数据的时候只有读和写命令。擦除命令则是当 SSD 控制器需要回收 stale 页来获取空闲空间的时候,由其垃圾回收进程触发。
写入的例子
让我们用个例子来理清 3.1 节的这些概念。下边的图是向 SSD 写入的一个例子。只显示了两个块,每个块有 4 个页。显然这是一个为了简化我在这使用的例子而精简的 NAND 闪存封装示意。图中的每一步里,图右侧的圆点解释了发生的事情
写入放大
因为写入是按页大小对齐的,任何没有对齐一个或者多个页大小的写操作都会写入大于所需的数据,这是写入放大的概念[13]。写一个字节最终导致一整页都要写入,而一页的大小在某些型号的 SSD 中可能达到 16KB,这是相当没有效率的。 而这不是唯一的问题。除了写入过多的数据外,这些额外的写入也会触发更多不必要的内部操作。实际上,用未对齐的方法写入数据会导致在更改和写回硬盘之前需要页读到缓存,这比直接写入硬盘要慢。这个操作被称为读-改-写,且应该尽可能的避免[2, 5]。
绝不进行少于一页的写入
避免写入小于 NAND 闪存页大小的数据块来最小化写入放大和读-改-写操作。现在一页的大小最大的是 16KB,因此这个值应作为缺省值使用。闪存页大小的值基于 SSD 型号并且在未来 SSD 发展中可能会增加。
对齐写入
以页大小对齐写入,并写入大小为数个页大小的数据块。
缓冲小的写入
为了最大化吞吐量,尽可能的将小数据写入 RAM 缓存中,当缓存满了之后执行一个大的写入来合并所有的小写入。
损耗均衡
如我们在1.1 节讨论的那样,NAND 闪存单元因其有 P/E 循环限制导致其有生命限制。想象一下我们有一个 SSD,数据总是在同一个块上写入。这个块将很快达到其 P/E 循环限制、耗尽。而 SSD 控制器井标记其为不可用。这样硬盘的容量将减小。想象一下买了一个 500GB 的硬盘,过了几年还剩 250G,这会非常恼火。
因此,SSD 控制器的一个主要目标是实现损耗均衡,即是将 P/E 循环在块间尽可能的平均分配。理想上,所有的块会在同一时间达到 P/E 循环上限并耗尽。[12, 14]
为了达到最好的全局损耗均衡,SSD 控制器需要明智的选择要写入的块,且可能需要在数个块之间移动,其内部的进程会导致写入放大的增加。因此,块的管理是在最大化损耗均衡和最小话写入放大之间的权衡。
制造商想出各种各样的功能来实现损耗均衡,例如下一节要讲的垃圾回收。
损耗均衡
因为 NAND 闪存单元会耗尽,FTL 的一个主要目标是尽可能平均的将工作分配给各个闪存单元,这样使得各个块将会在同一时间达到他们的 P/E 循环限制而耗尽。
闪存转换层(FTL)
FTL 存在的必要性
使用 SSD 如此容易的主要因素是其使用和 HDD 相同的主机接口。尽管一组逻辑块地址(LBA)的出现使其感觉像 HDD 的扇区一样可被复写,但其并非完全符合闪存的工作方式。因此需要一个额外的组件来隐藏 NAND 闪存的内部特征,并只向主机暴露一组 LBA。这个组件称为闪存转换层(FTL),位于 SSD 控制器中。FTL 很关键,并有两个主要的作用,逻辑块寻址和垃圾回收。
逻辑块映射
逻辑块映射将来自主机空间的逻辑块地址(LBA)转换为物理 NAND 闪存空间的物理块地址(PBA)。为了访问速度,这个映射表保存在 SSD 的 RAM 中,并保存在闪存中以防电源故障。当 SSD 启动后,这个表从闪存中读出并在 SSD 的 RAM 中重建[1, 5]。
一个比较简单的方法是使用页级映射来将主机的所有逻辑页映射为物理页。这个映射方法提供了很大的灵活性,然而主要的缺点是映射表需要大量的内存,这会显著地增加生产成本。一个解决方案是使用块级映射不再对页,而是对块进行映射。假设一个 SSD 硬盘每个块有 256 个页。这表示块级映射需要的内存是页级映射的 256 分之一,这是内存使用的巨大优化。然而这个映射仍然需要保存在硬盘上以防掉电。同时,以防大量小更新的工作负载,无论页是否是满的,全部闪存块都会写入。这会增加写入放大并使得块级映射普遍低效[1, 2]。
页级映射和块级映射的折中其实是在性能和空间之间折中的一个表现。一些研究者试着在两个方面都能够最佳化,得到了称为“hybrid(混合)”的方法[10]。最普遍的是日志块映射,其使用了一种比较像日志结构文件系统的方法。输入的写操作按顺序写入日志块中。当一个日志块满了之后,将其和与其在相同逻辑块编号(LBN)的数据块合并到空块中。只需要维护少量的日志块,且允许以页粒度维护。而块级映射是以块粒度维护的。
下边的图 5 是对混合日志块 FTL 的一个简单的陈述,每个块有 4 个页。FTL 处理 4 个写操作,都是页大小尺寸的写入。逻辑页编号 5 和 9 都被映射到 LBN(逻辑块编号)=1,而 LBN=1 关联到物理块#1000。最初,在逻辑块映射表中,LBN=1 的所有物理页 offset 都是 null,而日志块#1000 同样是完全空的。
第一个写入将 b’写到 LPN=5,这被日志块映射表解释到 LBN=1,即为关联到 PBN=1000(日志块#1000)。因此页 b’写到块#1000 的 0 号页中。映射用的元数据需要更新,为此,逻辑 offset 1(随便举的例子)对应的物理 offset 从 null 改为 0。
写入操作继续而映射元数据随之更新,当日志块#1000 完全填满,将其和对应为相同的逻辑块的数据块(本例中是#3000)合并。这个数据可以从数据块映射表中读取,此表将逻辑块映射为物理块。合并操作的结果数据被写到新的空块中,本例中为#9000。当这个工作做完了,块#1000 和#3000 可以被擦除成为空块,而块#9000 成为一个数据块。数据块映射表中 LBN=1 的元数据则从一开始的#3000 改为新的数据块#9000.
一个值得注意的很重要的事情是,四个写操作只集中在两个 LPN 中。逻辑块方法在合并的过程中隐藏了 b’和 d’操作,直接使用更加新的 b”和 d”版本,使其能够更好的降低写入放大。最后,如果读命令请求一个最新更新但还没合并到数据块中的页,这个页将会在日志块中读取,否者将会在数据块中找到。这就是如图 5 中所示,读操作即需要读取日志块映射表又需要读取数据块映射表的原因。
日志块 FTL 可以进行优化,其最值得注意的是切换合并,有时候也叫做“交换合并”。假设逻辑块中所有地址都立马写满了,这表示这些地址的所有新数据都将写到一个像样的日志块中。既然这个日志块包含的数据是一整个逻辑块的数据,那么将其和数据块合并到新的空块中是没有意义的,因为保存合并结果的空块中的数据就是日志块中的数据。只更新数据块映射表中的元数据并将数据块映射表中的数据块切换为日志块将会更快,这就是切换合并。
很多论文都讨论日志块映射方案,这些论文导致了一系列的改进,例如 FAST(Fully Associative Sector Translation 完全关联扇区转换)、superblock mapping(超块映射)、以及 flexible group mapping(灵活组映射)。同样还有其他的映射方案,例如 Mitsubishi(三菱)算法和 SSR。下面两篇论文是学习更多关于 FTL 和映射方案的很好的开始点
- “A Survey of Flash Translation Layer“, Chung et al., 2009 [9]
- “A Reconfigurable FTL (Flash Translation Layer) Architecture for NAND Flash-Based Applications“, Park et al., 2008 [10]
闪存转换层
闪存转换层是 SSD 控制器的一个组件,它将来自主机的逻辑块地址(LBA)映射为硬盘上的物理块地址(PBA)。大部分最新的硬盘使用了一种叫做“混合日志块映射”的技术或者其衍生技术,其工作方式比较像日志结构文件系统。这种技术可以将随机写入当做序列写入处理。
关于行业状态的注记
在 2014 年 2 月 2 日,Wikipedia [64]上列出了 70 个 SSD 的生产商,有意义的是,只有 11 个主控的生产商[65]。在这 11 个主控生产商之中,只有 4 个是“captive(自给自足的)”,即仅对它们自己的产品使用自己的主控(譬如 Intel 和三星);而其他的 7 个是“independent(独立的)”,即他们把自己的主控卖给其他的硬盘生产商。这些数字表示,这 7 个公司生产固态硬盘市场上 90%的主控。
我没有这 90%中,关于哪个主控生产商卖给哪个硬盘生产商的数据吗,但是根据帕雷托法则(二八定律或 80/20 法则——译注),我赌只有两三家主控制造商分享了大部分的蛋糕。直接结论是,来自非自给自足厂家的 SSD,因其本质上使用相同的主控或至少其主控使用相同的全局设计和底层思想,其行为会及其相似。
映射方案作为主控的一部分是 SSD 十分重要的组件,因为其往往完全定义了硬盘的表现。这就解释了为什么在有如此多竞争的产业中,SSD 主控生产商不分享其 FTL 实现的细节。因此,即使有大量的公共可用的关于 FTL 算法的研究,但仍然不清楚主控生产商使用了多少研究,以及某个牌子和型号具体实现了什么。
这篇文章[3]的作者声称通过分析工作负载,他们可以对硬盘的映射策略进行反向工程。除非芯片中的二进制代码本身被反向工程,我自己还是不太认同这种方法。因为没有办法完全确认某个硬盘中映射策略真正做了什么。并且预测一个特定的工作负载下映射的行为更难。
因为有很大量的不同映射策略,而对市场上所有可用固件进行反向工程所花费的时间的量值得考虑。然后,即使获得了所有可能的映射政策的源代码,你能拿来干啥?新产品的系统需求经常导致全面改进,使用未标明和内部易变的硬件组件。因此,只为一个映射政策进行优化是不值当的,因为这个优化的方案可能在其他所有映射政策表现很差。某人想只对一种映射政策进行优化的唯一原因是他在为已经保证使用一致硬件的嵌入式系统进行开发。
综上所述,我得说知道某一个 SSD 具体使用的是哪一个映射政策并不重要。唯一重要的事情是知道映射政策是 LBA 和 PBA 之间的转换层,而其很像混合日志块或其衍生算法的实现。因此,写入大小至少是 NAND 闪存块大小的数据片将会更加效率,因为对于 FTL 来说,更新映射及其元数据的开支是最小化的。
垃圾回收
如我在 4.1 和 4.2 节中所说,页不能被复写。如果页中的数据必须更新,新版本必须写到空页中,而保存之前版本数据的页被标记为 stale。当块被 stale 页充满后,其需要在能够再写入之前进行擦除。
垃圾回收
SSD 控制器中的垃圾回收进程确保“stale”的页被擦除并变为“free”状态,使得进来的写入命令可以访问这个页。
如第一节中所说,擦除命令需要 1500-3500 μs,写入命令需要 250-1500 μs。因为擦除比写入需要更高的延迟,额外的擦除步骤导致一个延迟使得写入更慢。因此,一些控制器实现了后台垃圾回收进程,或者被称为闲置垃圾回收,其充分利用空闲时间并经常在后台运行以回收 stale 页并确保将来的前台操作具有不足够的空页来实现最高性能[1]。其他的实现使用并行垃圾回收方法,其在来自主机的写入操作的同时,以并行方式进行垃圾回收操作。 遇到写入工作负载重到垃圾回收需要在主机来了命令之后实时运行的情况并非罕见。在这种情况下,本应运行在后台的垃圾回收进程可能会干预到前台命令[1]。TRIM 命令和预留空间是减少这种影响的很好的方法,具体细节将在 6.1 和 6.2 节介绍。
后台操作可能影响前台操作
诸如垃圾回收后台操作可能会对来自主机的前台操作造成负面影响,尤其是在持续的小随机写入的工作负载下。 块需要移动的一个不太重要的原因是 read disturb(读取扰乱)。读取可能改变临近单元的状态,因此需要再一定数量的读取之后移动块数据[14]。
数据改变率是一个很重要的影响因素。有些数据很少变化,称为冷数据或者静态数据,而其他一些数据更新的很频繁,称为热数据或者动态数据。如果一个页一部分储存冷数据,另一部分储存热数据,这样冷数据会随着热数据一起在垃圾回收以损耗均衡的过程中拷贝,冷数据的存在增加了写入放大。这可以通过将冷数据从热数据之中分离出来,存储到另外的页中来避免。缺点是这样会使保存冷数据的页更少擦除,因此必须将保存冷数据和热数据的块经常交换以确保损耗均衡。
因为数据的热度是在应用级确定的,FTL 没法知道一个页中有多少冷数据和热数据。改进 SSD 性能的一个办法是尽可能将冷热数据分到不同的页中,使垃圾回收的工作更简单。
分开冷热数据
热数据是经常改变的数据,而冷数据是不经常改变的数据。如果一些热数据和冷数据一起保存到同一个页中,冷数据会随着热数据的读-改-写操作一起复制很多次,并在为了损耗均衡进行垃圾回收过程中一起移动。尽可能的将冷热数据分到不同的页中是垃圾回收的工作更简单
缓存热数据
极其热的数据应该尽可能多的缓存,并尽可能的少的写入到硬盘中。
以较大的量废除旧数据
当一些数据不再需要或者需要删除的时候,最好等其它的数据一起,在一个操作中废除一大批数据。这会使垃圾回收进程一次处理更大的区域而最小化内部碎片。
高级功能
TRIM
让我们假设一个程序向 SSD 所有的逻辑块地址都写入文件,这个 SSD 当然会被装满。然后删除这些文件。文件系统会报告所有的地方都是空的,尽管硬盘实际上还是满的,因为 SSD 主控没法知道逻辑数据是什么时候被主机删掉的。SSD 主控只会在这些逻辑块地址被复写的时候才知道这些是空闲空间。此时,垃圾回收进程将会擦除与这些文件相关的块,为进来的写操作提供空的页。其结果就是,擦除操作并非在知道保存有无用数据之后立刻执行,而是被延迟了,这将严重影响性能。
另一个值得关心的是,既然 SDD 主控不知道这些页保存有已删除的文件,垃圾回收机制仍然会为了损耗均衡而移动这些页上的数据。这增加了写入放大,并毫无意义地影响了来自主机的前台工作负载。
延迟擦除问题的一个解决方法是 TRIM 命令,这个命令由操作系统发送,通知 SSD 控制器逻辑空间中的这些页不会再使用了。有了这个信息,垃圾回收进程就会知道自己不必再移动这些页,并可以在任何需要的时间擦除它们。TRIM 命令只会在当 SSD、操作系统和文件系统都支持的时候才起作用。
TRIM 命令的维基百科页面列出了支持 TRIM 的操作系统和文件系统[16]。Linux 下,ATA TRIM 的支持是在 2.6.33 版本加入的。尽管 ext2 和 ext3 文件系统不支持 TRIM,ext4 和 XFS 以及其他的一些是支持的。在 Mac OS 10.6.8 下,HFS+支持 TRIM 操作。Windows 7 则只支持使用 SATA 接口的 SSD 的 TRIM,使用 PCI-Express 的则不支持。
现在大部分的硬盘都支持 TRIM,确实,允许垃圾回收尽早的工作显著地提升了将来的性能。因此强烈建议使用支持 TRIM 的 SSD,并确保操作系统和文件系统级都启用了 TRIM 功能。
预留空间
预留空间只是简单的使物理块比逻辑块多,即为主控保留一定比例的,用户不可见的物理块。大多专业级 SSD 生产商已经包括了一些预留空间,通常是 7~25%[13]。用户可以简单的通过创建比最大物理容量小的逻辑容量分区来创建更多的预留空间。例如,你可以在 100G 的硬盘上创建一个 90G 的分区,而把剩下的 10G 作为预留空间。即使预留空间在操作系统级是不可见的,但 SSD 主控仍然是可以看见的。生产商提供预留空间的主要原因是为了对付 NAND 闪存单元固有的兽兽门限制。不可见的预留空间的块将无缝的替换可见空间上的已耗损殆尽的块。
AnandTech 有一篇有意思的文章显示出预留空间对 SSD 寿命和性能的影响[34]。在他们研究的硬盘上,结果显示出仅通过保证 25%的预留空间(把所有的预留空间加在一起)就可以使性能极大地提升。在 Percona 的一篇文章中有另外一个有意思的结果,在他们测试了一块 Intel 320 SSD,显示出当硬盘逐渐填满时,写入吞吐量将随之下降[38]。
这里是我的实验中发生的事情。垃圾回收是使用空闲时间在后台擦除页上不再使用的数据的。但既然擦除操作比写入操作的延迟更久,或者说擦除要的时间比写入长,在持续的重随机写入工作负载下的 SSD 将会在垃圾回收有机会擦除之前用尽所有的空块。此时,FTL 将不能跟上前台的随机写入工作负载,而垃圾回收进程将必须在写入命令进来的同时擦除块。这就是在基准测试中 SSD 性能下降的厉害,而 SSD 显得性能超差的时候,如下图 7 所示。因此,预留空间可以吸收高吞吐量写入负载,起到缓冲的作用,为垃圾回收跟上写入操作并重新开始擦除无用块留够时间。预留空间需要多少大部分基于 SSD 使用环境的工作负载,和其需要的承受的写入操作的量。作为参考,持续随机写入的工作负载比较推荐使用大约 25%的预留空间[34]。如果工作负载不是很重,大概 10~15%估计够大的了。
预留空间对损耗均衡和性能表现是有用的
SSD 可以简单地通过格式化时设置比最大物理容量小的逻辑容量来启动预留空间。余下的不被用户所见的空间仍会被 SSD 主控所用。预留空间帮助损耗均衡机制来对付 NAND 闪存单元的寿命限制。对于写入不是很重的工作负载,10~15%的预留空间足够了。对于持续的随机写入工作负载,保持 25%的预留空间将会提升性能。预留空间将会扮演 NAND 闪存块的缓冲区的角色,帮助垃圾回收进程吸收写入峰值。
从此,同样可以推导出,预留空间甚至可能为不支持 TRIM 命令的环境提供更大的性能提升——注意我只是在这进行个假设,我仍然在寻找材料来支持这个观点。假设只有 75%的硬盘空间可被操作系统使用,余下的 25%留作预留空间。因为 SSD 控制器可以看见整个硬盘,尽管在某一时刻只有 75%的物理 NAND 闪存被使用,但 100%的块都在已使用、已废弃、已擦除三种状态中交替转换。这表示余下的 25%物理闪存将可以安全的认为没有保存有数据,因为其并没有被映射到任何逻辑块地址上。因此,即使不支持 TRIM,垃圾回收进程也能够提前擦除这些预留空间中块。
安全擦除
一些 SSD 主控提供 ATA 安全擦除功能,其作用是当硬盘处于意料之外的状态时恢复其性能。这个命令擦除所有用户写入的数据并重置 FTL 映射表,但这显然不能克服 P/E 循环有限造成的物理限制。尽管其功能上看上去非常有前途,但需要每个生产商争取的实现它。Wei 等人 2011 年在他们关于安全擦除命令的 review 中显示,在他们研究的超过 12 个型号 SSD 中,只有 8 个提供了 ATA 安全擦除功能,而在这 8 个硬盘中,三个都有各种 bug[11]。
对于性能的影响是很重要的,并且在安全方面更加重要,但我不想展开说。这里有几个 Stack Overflow 上的讨论可以解释关于如何可靠地删除 SSD 中的数据的细节。
原生命令队列(NCQ)
原生命令队列(NCQ)是 SATA 的一个功能,其允许 SSD 接受来自主机的多个命令,使 SSD 可以使用内部并行同时完成这些命令[3]。除了减少因为硬盘造成的延迟外,一些新的硬盘同样使用 NCQ 来应对来自主机的延迟。例如 NCQ 可以优先传入的命令来确保当主机 CPU 忙的时候硬盘总是有命令处理。
断电保护
无论是在家还是在数据中心,断电都是有可能发生的。一些生产商在他们的 SSD 结构中设置有超级电容,这个电容设计为存有足够提交总线中所有 I/O 请求所需的能量以防掉电。问题在于并非所有的 SSD 生产商都为他们的硬盘设置超级电容或者某种掉电保护,而有超级电容的不总是在说明书中提及。然后,和安全擦除命令一样,断电保护机制的实现是否正确,并且是否确实能够在掉电时避免数据损坏是搞不清楚的。
Zheng 等人 2013 年的研究中测试了 15 个 SSD,但没有透露品牌[72]。他们给硬盘各种各样的电源故障,发现测试的 15 个 SSD 中有 13 个最终丢失数据或者大规模的数据损坏。在另外一篇 Luke Kenneth Casson Leighton 写的关于电源故障的文章中显示出,测试的 4 个硬盘有 3 个最终都在不正确的状态,剩下的一个没有问题(是 Intel 的硬盘)[73] 。
SSD 还是一个非常年轻的技术,并且我相信这些电源故障下造成数据损坏的阻力将会在接下来的产品中克服。然而现在,在数据中心配置不间断电源(UPS)可能还是应该做的。并且和任何其他存储解决方案一样,经常备份敏感数据。
SSD 中的内部并行
总线带宽限制
因为物理限制的存在,异步 NAND 闪存 I/O 总线无法提供 32-40 MB/s 以上的带宽[5]。SSD 生产商提升性能的唯一办法是以某种方法让他们的硬盘中的多个存储芯片可以并行或者交错。[2]的 2.2 节中有一个比较好的关于交错的解释。
通过组合 SSD 内所有层次的内部并行,不同芯片中的多个块可以作为一个叫 clustered block(直译是集群块,但我感觉用簇比较亲切,本译文中所有的簇都是指它)东西同时访问。我不打算解释关于 SSD 内部并行的所有细节,因此我只简述一下并行的层次和簇。获取更多关于这个话题的信息,以及 SSD 内部并行的概括,这里的两篇论文是很好的开始[2, 3]。此外,一些高级命令如 copyback 和 inter-plane transfer 在[5]中有阐述。
内部并行
在 SSD 内部,数个层次的并行允许一次将数个块写入到不同的 NAND 闪存芯片中,这些块称为簇。
并行的多级别
下边的图 6 展示了 NAND 闪存芯片的内部,其组织为一种分级的结构。这些级别包括通道、封装、芯片、面、块和页。如[3]中揭示的,这些不同的层通过下边的方法提供并行
- 通道级并行 闪存主控和闪存封装之间的通信通过数个通道。这些通道可以独立或者同时访问。每个独立通道有数个封装共享。
- 封装级并行 一个通道中的不同封装可以独立访问。交错可以使命令同时在同一个通道中的不同封装中运行。
- 芯片级并行 一个封装包含两个或者更多的芯片,芯片可以并行独立访问。注:芯片通常也被称为核心(chips are also called “dies”)
- 面级并行 一个芯片包含两个或者更多的面。相同的操作(读、写或者擦除)可以在芯片中多个面上同时运行。面包含了块,块包含了页。面也包含了寄存器(小 RAM 缓存),其用在面级操作上。
簇
从多个芯片中访问到的多个块被称为一个簇[2]。这个做法比较像 RAID 系统中的 striping[1, 5]。
一次访问的逻辑块地址被分到不同 SSD 闪存封装中的不同芯片上。这归功于 FTL 的映射算法,并且这与这些地址是否连续无关。分割块允许同时使用多个通道来整合其带宽,并同样可以并行执行多个读、写和擦除操作。这即表示 I/O 操作按簇大小对齐来确保 SSD 中多个级别的内部并行所提供的性能能够最大程度的利用。
访问模式
定义顺序和随机 I/O 操作
在接下来的部分,我打算从“顺序”或“随机”入手。如果 I/O 操作开始的逻辑块地址(LBA)直接跟着前一个 I/O 操作的最后 LBA,则称值为顺序访问。如果不是这样,那这个 I/O 操作称为随机访问。这很一点重要,因为 FTL 执行动态映射,相邻的逻辑空间地址可能被应用于不相邻的物理空间地址上。
写入
基准测试和生产商提供的数据表显示出,随机写入比序列写入要慢,但这并不总是对的,因为随机写入的速度实际上取决于工作负载的类型。如果写入比较小,小是说小于簇(译注:关于簇的翻译请见上一篇文章)大小(就是说 <32MB),那么是的,随机写入比顺序写入慢。然而,如果随机写入是按照簇大小对齐的,其性能将会和顺序写入一样。
解释如下。如第六节所说,SSD 的内部并行机制通过并行和交错,允许簇中的块同时访问。因此,无论是随机或者序列写入,都会同样将数据写入到多个通道和芯片上,从而执行簇大小的写入可以确保全部的内部并行都用上了。簇上的写入操作将在后边的 7.3 节解释。至于性能,如来自 [2]和 [8] 的图 8 和图 9 所示。当基准测试写入缓存和簇大小(大部分 SSD 是 16 或 32MB)相同或者更大时,随机写入达到和顺序写入同样高的吞吐量。
然而,如果是小写入——小是指比 NAND 闪存页小,就是说< 16 KB——主控需要做更多的工作以维护用来做块映射的元数据上。确实,一些 SSD 使用树形的数据结构来实现逻辑块地址和物理块地址之间的映射[1],而大量小随机写入将转换成 RAM 中映射的大量更新。因为这个映射表需要在闪存中维护[1, 5],这将导致闪存上的大量写入。而顺序工作负载只会导致少量元数据的更新,因此闪存的写入较少。
随机写入并不总是比顺序写入慢
如果写入很小(就是说比簇大小要小),随机写入将比顺序写入慢。如果写入是按簇大小对齐,随机写入将使用所有可用层级上的内部并行,并显示出和随机写入相同的性能。
另外一个原因是,如果随机写入很小,其将在块中引起大量的复制-擦除-写入操作。另一方面,大于等于块大小的顺序写入可以使用更快的交换合并优化操作。再者,小随机写入显然会有随机的无效数据。大量的块将只有一页是无效的,而非只有几个块全部无效,这样会导致作废的页将遍布物理空间而非集中在一起。这种现象被称为内部碎片,并导致清除效率下降,垃圾回收进程通过请求大量的擦除操作才能创建空页。
最后关于并发性,已有的研究已经显示出,单线程写入大数据和用很多并非线程写入大量小数据是一样快的。确实,大写入可以确保 SSD 所有的内部并行都用上了。因此试着实现并发的多个写入并不会提高吞吐量[1, 5]。然而,多并行写入和单线程访问相比将会有延迟[3, 26, 27]。
一个单一的大写入比很多小的并发写入要好
单一的大写入请求和很多小并发写入请求相比,表现出相同的吞吐量,但会导致延迟。单一的大写入比并发写入在响应时间上表现的更好。因此,只要可能,最好使用大写入.
当写入小并且没有经过组织或缓存,多线程比较好
很多并发的小写入请求将比单一的小写入请求提供更好的吞吐量。因此如果 I/O 比较小并不能整合到一起,最好是使用多线程。
读取
读取比写入要快。无论是顺序读取还是随机读取,都是这样。FTL 是逻辑块到物理块地址的动态映射,并且将写入分布到各个通道上。这个方法有时候被称为“基于写入顺序的”映射[3]。如果数据是以和原本写入的顺序完全不相关,完全随机读取的,那就无法保证连续的读取分布在不同的通道。甚至有可能连续的随机读取访问的是同一个通道中的不同块,因此无法从内部并行中获取任何优势。Acunu 写了一篇博文讲了这个情况,至少在他们测试的硬盘上是这样,读取性能和读取访问模式与数据原始写入方式有多相似直接挂钩[47]。
为了提升读取性能,将相关数据写在一起
读取性能由写入模式决定。当大块数据一次性写入时,其将被分散到不同的 NAND 闪存芯片上。因此你应该将相关的数据写在相同的页、块、或者簇上,这样稍后你可以利用内部并行的优势,用一个 I/O 访问较快的读取。
下面的图 10 表示出一个有两个通道 4 块芯片,每块芯片只有一个面的 SSD。注意这技术上是不成立的,因为 SSD 每块芯片都有两个以上的面,但为了保持图片简洁,我决定只让每块芯片只有一面。大写字母代表大小和 NAND 闪存块相同的数据。图 10 上边的操作是顺序写入 4 个块:[A B C D],在这个例子里刚好是一个簇的大小。写操作通过并行和交错被分配到四个面上使其更快。即便 4 个块在逻辑块地址上是连续的,但他们存储在四个不同的面中。
基于写入顺序的 FTL,面上所有的块被选作写入操作的可能是近乎于相同的,因此簇不必由各自面上相同物理块地址(PBN)的块组成。在图 10 的例子中,第一个簇由四个不同面上的块组成,各自面上的 PBN 分别是 1, 23, 11, 和 51。
图 10 下边有两个读取操作,[A B E F] 和 [A B G H]。对于[A B E F]来说,A 和 E 在同一个面上,这使其必须只从一个通道的两个面上读取。对于[A B G H]来说,A、B、G、和 H 存储在四个不同的面上,因此[A B G H]能够同时从两个通道的四个面上读取。从更多的面和通道上读取可以从内部并行上获得更多的优势,从而有更好的读取性能。
内部并行的一个直接结果是,使用多线程同时读取数据不是提升性能所必须的。实际上,如果这些并不知道内部映射的线程访问这些地址,将无法利用内部并行的优势,其可能导致访问相同的通道。同时,并发读取线程可能影响 SSD 预读能力(预读缓存)[3]。
一个单一的大读取比很多小的并发读取要好
并发随机读取不能完全使用预读机制。并且,多个逻辑块地址可能被映射到相同的芯片上,不能利用内部并行的优势。再者,一个大的读取操作会访问连续的地址,因此能够使用预读缓存(如果有的话)。因此,进行大读取请求更加可取。
SSD 生产商通常不说页、块和簇的大小。但可以通过运行简单的工作负载来进行反向工程获取大部分 SSD 的基础特征[2, 3]。这些信息可以用在优化读写操作的缓存大小上,同时可以在格式化硬盘的时候使得分区与 SSD 内部特征对齐,如 8.4 节中所说。
并发读写
小的读和写交错会导致性能下降[1, 3]。其主要原因是对于同一个内部资源来说读写是相互竞争的,而这种混合阻止了诸如预读取机制的完全利用。
分离读写请求
混合了小读取和小写入的工作负载将会阻止内部缓存和预读取机制的正常工作,并导致吞吐量下降。最好是能够避免同时的读写,并以一个一个的较大的数据块来进行,最好是簇的大小。例如,如果必须更新 1000 个文件,你可以遍历这些文件,对每个文件进行读和写然后移动到下一个文件,但这将会很慢。如果一次读取全部 1000 个文件然后一次写入 1000 个文件会更好。
系统优化
分区对齐
如 3.1 节中解释的那样,写入是页对齐的。大小是页大小,并且和页大小是对齐的写入请求,会被直接写入到一个 NAND 闪存物理页中。大小是页大小,但不对齐的写入请求将会被写入到两个个 NAND 闪存物理页中,并导致两个读-改-写操作[53]。因此,确保用来写入的 SSD 分区是和硬盘采用的物理 NAND 闪存页的大小对齐是很重要的。很多教程和指引都讲了格式化的时候如何将分区对齐 SSD 的参数[54, 55]。稍微 Google 一下就能知道某型号的 SSD 的 NAND 闪存页、闪存块和簇的大小。就算是没法拿到这些信息,通过一些反向工程也可以揭示出这些参数[2, 3]。
有人指出分区对齐可以显著地提高性能[43]。还有人指出,在对某块硬盘的测试中,绕过文件系统对硬盘直接进行写入会提高性能,不过提高很小[44]。
分区对齐
为了确保逻辑写入真的是和物理存储是对齐的,必须将分区和硬盘的 NAND 闪存页大小对齐。
文件系统参数
如 5.1 节所说,并不是所有的文件系统都支持 TRIM 命令[16]。Linux 2.6.33 及以上,ext4 和 XFS 支持 TRIM,但仍需要使用 discard 参数来启用。此外,还有一些其它的微调,如果没有什么东西需要的话,可以通过移除 relatime 参数并添加 noatime, nodiratime [40, 55, 56, 57] 将元数据的更新关掉。
启用 TRIM 命令
确认你的核心和文件系统支持 TRIM 命令。当某个块被删除的时候,TRIM 命令会通知 SSD 主控。垃圾回收进程可以在闲的时候后台擦除这些块,为面对大写入工作负载准备硬盘。
操作系统 I/O 调度器
Linux 上的默认 I/O 调度器是 CFQ 调度器(Completely Fair Queuing 完全公平队列)。CFQ 被设计用来通过将物理上接近的 I/O 请求组合到一起从而最小化机械硬盘寻道时间的。这种请求重新排序对于 SSD 是 不必要的,因为它们没有机械部分。几个指引和讨论文章主张将 I/O 调度器从 CFQ 换为 NOOP 或 Deadline 将会减少 SSD 的延迟[56, 58]。然而子 Linux 3.1 版之后,CFQ 为固态硬盘提供了一些优化[59]。基准测试同样显示出调度器的性能取决于应用在 SSD 上的工作负载(即应用),和硬盘自身[40, 60, 61, 62]。
我个人从中学到的是,除非工作负载十分特殊并且特定应用的基准测试显示出某个调度器确实比另一个好,CFQ 是一个比较安全的选择。
交换分区
因为相当数量的 I/O 请求是由向硬盘上进行页交换导致的,SSD 上的交换分区会增加损耗并显著降低寿命。在 Linux 内核,vm.swappiness 参数控制想硬盘交换页的频率。其可用值是 0 到 100,0 表示内核需要尽可能的避免交换。以 Ubuntu 来说,默认 swappiness 是 60。当使用 SSD 的时候,尽可能降低这个值(就是说设为 0)会避免不必要的向硬盘的写入并增加其寿命[56, 63]。一些教程建议设置为 1,而在实践中实际上和 0 一样的[57, 58]。 另外的做法的使用内存作为交换分区,或者完全避免使用交换分区。
临时文件
所有临时文件和日志文件没有必要,否则只是浪费 SSD 的生命周期。一些可以保存在 RAM 中的文件可以使用 tmpfs 文件系统[56, 57, 58]。
总结—每个程序员都应该了解的关于固态硬盘的知识
基础
存储单元类型
固态硬盘(SSD)是基于闪存的数据存储设备。每个比特都存储在存储单元中,而存储单元分为三类:每个单元 1 比特(单层单元,SLC),每个单元 2 比特(多层单元,MLC),每个单元 3 比特(三层单元,TLC)。
寿命限制
每个单元都有 P/E(写/擦)循环的最大限制,在此之后存储单元被认为是损坏的。这表示 NAND 闪存已经损耗殆尽,并有一个寿命限制。
基准测试很难
测试者是人,因此并不是所有的基准测试都毫无破绽。在读生产商或者第三方的基准测试结果的时候请小心,并在相信这些数据之前使用多个来源的数据。在有可能的时候,使用你的系统特定的工作负载在你打算使用的 SSD 型号上,运行自己的内部基准测试。最后,着眼于与你的系统最相关的性能指标。
页和块
NAND 闪存页和块
闪存单元组织成为阵列,称为块,而块组织成为面。块中能够进行读写操作的最小单元是页。页不能独立擦除,只能整块擦除。NAND 闪存页大的大小并不一致,大多数硬盘的页大小是 2KB、4KB、8 KB 或 16 KB。大多数 SSD 每个块有 128 或 256 个页。这即表示一个块的大小可能在 256 KB 到 4 MB 之间。例如 Samsung SSD 840 EVO 的块大小是 2048 KB,每块包括 256 页每页 8KB。
读是页对齐的
一次读取少于一页是不可能的。当然可以通过操作系统只请求一个直接,但 SSD 中会取回整个页,强制读取比所需多的多的数据。
写是页对齐的
写入到 SSD 的时候,写入将补齐到页大小。所以即便写入操作只影响一字节,都会重写整个页。写入比所需更多的数据被称为写入放大。写入一个页也被称为“to program”一个页。
页不能覆写
NAND 闪存页只能在其为“空”的状态下进行写入。当数据改变,页的内容被复制到内部寄存器中,数据更新,然后新版本的数据将存储字啊一个”空“页中,这个操作被称为”读-改-写“。数据并非原地更新,因为”空”页并非原先存储数据的页。一旦数据被保存到硬盘上,原来的页将被标记为“废弃”,并一直保持这样直到被擦除。
擦除是块对齐的
页不能被覆写,一旦页成为“废弃的”,让其重新空下来的方法是擦除它们。然而,独立擦除一个页是不可能的,并且只能一次擦除整个块。
主控和内部
闪存转换层
闪存转换层(FTL)是 SSD 主控中的一个组件,它将来自主机的逻辑块地址(LBA)映射为硬盘上的物理块地址(PBA)。很多最新的硬盘使用了类似于“混合日志块”或其派生者的技术,其以类似于日志结构文件系统的方式工作。这允许将随机写入当做顺序写入处理。
内部并行
在内部,数个层次的并行使得可以一次向不同 NAND 闪存芯片上写数个块的数据,这几个块被称为“簇”。
损耗均衡
因为 NAND 闪存单元会损耗殆尽,FTL 的一个主要目标是额能的将工作分散到各个单元上,这样块中的闪存单元可以在同一时间损坏。
垃圾回收
SSD 主控中的垃圾回收确保废弃的块能够擦除并重新变为空状态,使新进入的写入命令可以访问。
后台操作可能影响前台操作
例如垃圾回收的后台操作可能对来自主机的前台操作有负面影响,尤其是在持续的小随机写入工作负载下。
访问模式
绝不写入少于一页的数据
避免写入少于 NAND 闪存页大小的数据以最小化写入放大并避免读-改-写操作。目前一页最大为 16KB,因此此值应该作为默认值使用。这个值取决于 SSD 型号,并且在将来 SSD 改进之后你可能需要增加。
对齐写入
将写入对齐为页大小,写入数据块大小为页大小的倍数
小写入缓存化
为了最大化吞吐量,在任何可能的时候将小写入缓存到内存中,当缓存满了再进行一次大写入,这样来将小写入打包。
为了提升读性能,将相关的数据写在一起
读取性能取决于写入模式。当大数据块一次写入的时候,其将被分配到不同的 NAND 闪存芯片上。因此你需要将相关的数据写到相同的页、块、或簇上,这样在你之后读取的时候可以利用内部并行,用一个 I/O 请求更快的读取。
分离读写请求
混合的小读写交叉的工作负载会妨碍内部缓存和预读取机制正常工作,并会导致吞吐量下降。最好的办法是避免同时发生的读操作和写操作,并将其以一个接一个的大数据块的形式实现,数据块大小推荐和簇大小相同。举个例子,如果要更新 1000 个文件,你可以遍历文件逐一读写,但会很慢。如果一次读取 1000 个文件然后一次写回 1000 个文件将会好很多。
批量废弃数据
当有数据不再需要或者需要删除的时候,最好等到废弃数据量比较大的时候在一个操作中统一废弃它们。这可以使垃圾回收进程可以一次处理更大范围的数据,可以最小化内部碎片。
随机写入并不远总是比顺序写入慢
如果写入很小(即小于簇大小)。随机写入将比顺序写入慢。如果写入都是数倍于且对齐簇大小的话,随机写入将会利用所有可用层次的内部并行,并达到和顺序写入相同的水平。对于大部分硬盘来说,簇大小是 16 或 32MB,因此使用 32MB 应该是安全的。
一个大的单线程的读取比很多小的并发读取要好
并发随机读取不能完全使用预读取机制。并且,多个逻辑块地址会指向同一个芯片而不能利用内部并行。一个大的书签操作将会访问连续地址并在预读取缓存可用的时候利用之,并使用内部并行。因此如果用例允许的话,发出一个大的读取请求会好一些。
一个大的单线程写入比很多小的并发写入要好
大的单线程写入请求提供的吞吐量和很多并发写入是相同的,然而就延迟来说,一个大的单一写入比并发写入反应时间要短一些。因此,在可用的时候,使用单线程大写入是最好的。
当写入小且无法缓存或组织在一起的时候,多线程是有用的
很多并发写入请求将比单一小写入请求提供的吞吐量要大。因此,如果 I/O 很小并且不能组织,用多线程比较好。
分离冷热数据
热数据是经常改变的数据,而冷数据是不经常改变的数据。如果一些热数据和冷数据被保存在同一个页中,冷数据将会随着热数据的每次读-改-写操作一起复制,并且会因为垃圾回收的损耗均衡不停移动位置。尽可能的将冷热数据分离到不同的也中将减轻垃圾回收的工作。
缓存热数据
极其热的速进和其他高频变化的元数据应该尽可能的缓存并尽可能少的写入硬盘。
系统优化
PCI Express 和 SAS 比 SATA 快
生产商提供的两个主要的接口是 SATA 3.0 (550 MB/s) 和 PCI Express 3.0 (1 GB/s 每通道,使用多个通道)。串行 SCSI(SAS)同样在企业级的 SSD 上可用。在最新版本中,PCI Express 和 SAS 比 SATA 要快,但也更贵。
预留空间对提高性能和损耗均衡十分有用
硬盘可以简单地通过在格式化的时候让逻辑分区容量比最大物理容量小来设置预留空间。预留的空间对用户不可见,但 SSD 主控仍然可见。预留空间帮助损耗均衡机制应付 NAND 闪存单元固有的寿命限制。对于写入负载不是很重的工作环境,10%~15%的预留空间足够了。对于持续随机写入的工作负载,保持 25%的工作负载将会提升性能。预留空间将会扮演 NAND 闪存块的缓存的角色,帮助垃圾回收进程吸收写入峰值。
启用 TRIM 命令
确认你的系统内核和文件系统支持 TRIM 命令。TRIM 命令在块被删除的时候通知 SSD 主控。当垃圾回收进程能够在空闲时候后台擦除这些块,为将来的写入负载准备硬盘。
对齐分区
为了确保逻辑写入确实和物理存储设备对齐,你需要将分区和硬盘的 NAND 闪存页对齐
总结
这是为“SSD 编程”系列文章的总结。我希望我用易于理解的方法表述了我在固态硬盘上的个人研究中学到的东西。 如果你读了这个系列并想深入了解 SSD,比较好的起步点是读一些我在第二部分和第五部分列出的引用文献和文章。 另外一个很好的学习资源是 FAST 大会(the USENIX Conference on File and Storage Technologies,USENIX 文件和存储技术大会)。会上每年都有大量出色的研究。我高度推荐他们的网站,FAST 2013 视频和文献的集散地。
Reference
[1] Understanding Intrinsic Characteristics and System Implications of Flash Memory based Solid State Drives, Chen et al., 2009
[2] Parameter-Aware I/O Management for Solid State Disks (SSDs), Kim et al., 2012
[3] Essential roles of exploiting internal parallelism of flash memory based solid state drives in high-speed data processing, Chen et al, 2011
[4] Exploring and Exploiting the Multilevel Parallelism Inside SSDs for Improved Performance and Endurance, Hu et al., 2013
[5] Design Tradeoffs for SSD Performance, Agrawal et al., 2008
[6] Design Patterns for Tunable and Efficient SSD-based Indexes, Anand et al., 2012
[7] BPLRU: A Buffer Management Scheme for Improving Random Writes in Flash Storage, Kim et al., 2008
[8] SFS: Random Write Considered Harmful in Solid State Drives, Min et al., 2012
[9] A Survey of Flash Translation Layer, Chung et al., 2009
[10] A Reconfigurable FTL (Flash Translation Layer) Architecture for NAND Flash-Based Applications, Park et al., 2008
[11] Reliably Erasing Data From Flash-Based Solid State Drives, Wei et al., 2011
[12] http://en.wikipedia.org/wiki/Solid-state_drive
[13] http://en.wikipedia.org/wiki/Write_amplification
[14] http://en.wikipedia.org/wiki/Flash_memory
[15] http://en.wikipedia.org/wiki/Serial_ATA
[16] http://en.wikipedia.org/wiki/Trim_(computing)
[17] http://en.wikipedia.org/wiki/IOPS
[18] http://en.wikipedia.org/wiki/Hard_disk_drive
[19] http://en.wikipedia.org/wiki/Hard_disk_drive_performance_characteristics
[20] http://centon.com/flash-products/chiptype
[21] http://www.thessdreview.com/our-reviews/samsung-64gb-mlc-ssd/
[22] http://www.anandtech.com/show/7594/samsung-ssd-840-evo-msata-120gb-250gb-500gb-1tb-review
[23] http://www.anandtech.com/show/6337/samsung-ssd-840-250gb-review/2
[24] http://www.storagereview.com/ssd_vs_hdd
[25] http://www.storagereview.com/wd_black_4tb_desktop_hard_drive_review_wd4003fzex
[26] http://www.storagereview.com/samsung_ssd_840_pro_review
[27] http://www.storagereview.com/micron_p420m_enterprise_pcie_ssd_review
[28] http://www.storagereview.com/intel_x25-m_ssd_review
[29] http://www.storagereview.com/seagate_momentus_xt_750gb_review
[30] http://www.storagereview.com/corsair_vengeance_ddr3_ram_disk_review
[31] http://arstechnica.com/information-technology/2012/06/inside-the-ssd-revolution-how-solid-state-disks-really-work/
[32] http://www.anandtech.com/show/2738
[33] http://www.anandtech.com/show/2829
[34] http://www.anandtech.com/show/6489
[35] http://lwn.net/Articles/353411/
[36] http://us.hardware.info/reviews/4178/10/hardwareinfo-tests-lifespan-of-samsung-ssd-840-250gb-tlc-ssd-updated-with-final-conclusion-final-update-20-6-2013
[37] http://www.anandtech.com/show/6489/playing-with-op
[38] http://www.ssdperformanceblog.com/2011/06/intel-320-ssd-random-write-performance/
[39] http://en.wikipedia.org/wiki/Native_Command_Queuing
[40] http://superuser.com/questions/228657/which-linux-filesystem-works-best-with-ssd/
[41] http://blog.superuser.com/2011/05/10/maximizing-the-lifetime-of-your-ssd/
[42] http://serverfault.com/questions/356534/ssd-erase-block-size-lvm-pv-on-raw-device-alignment
[43] http://rethinkdb.com/blog/page-alignment-on-ssds/
[44] http://rethinkdb.com/blog/more-on-alignment-ext2-and-partitioning-on-ssds/
[45] http://rickardnobel.se/storage-performance-iops-latency-throughput/
[46] http://www.brentozar.com/archive/2013/09/iops-are-a-scam/
[47] http://www.acunu.com/2/post/2011/08/why-theory-fails-for-ssds.html
[48] http://security.stackexchange.com/questions/12503/can-wiped-ssd-data-be-recovered
[49] http://security.stackexchange.com/questions/5662/is-it-enough-to-only-wipe-a-flash-drive-once
[50] http://searchsolidstatestorage.techtarget.com/feature/The-truth-about-SSD-performance-benchmarks
[51] http://www.theregister.co.uk/2012/12/03/macronix_thermal_annealing_extends_life_of_flash_memory/
[52] http://www.eecs.berkeley.edu/~rcs/research/interactive_latency.html
[53] http://blog.nuclex-games.com/2009/12/aligning-an-ssd-on-linux/
[54] http://www.linux-mag.com/id/8397/
[55] http://tytso.livejournal.com/2009/02/20/
[56] https://wiki.debian.org/SSDOptimization
[57] http://wiki.gentoo.org/wiki/SSD
[58] https://wiki.archlinux.org/index.php/Solid_State_Drives
[59] https://www.kernel.org/doc/Documentation/block/cfq-iosched.txt
[60] http://www.danielscottlawrence.com/blog/should_i_change_my_disk_scheduler_to_use_NOOP.html
[61] http://www.phoronix.com/scan.php?page=article&item=linux_iosched_2012
[62] http://www.velobit.com/storage-performance-blog/bid/126135/Effects-Of-Linux-IO-Scheduler-On-SSD-Performance
[63] http://www.axpad.com/blog/301
[64] http://en.wikipedia.org/wiki/List_of_solid-state_drive_manufacturers
[65] http://en.wikipedia.org/wiki/List_of_flash_memory_controller_manufacturers
[66] http://blog.zorinaq.com/?e=29
[67] http://www.gamersnexus.net/guides/956-how-ssds-are-made
[68] http://www.gamersnexus.net/guides/1148-how-ram-and-ssds-are-made-smt-lines
[69] http://www.tweaktown.com/articles/4655/kingston_factory_tour_making_of_an_ssd_from_start_to_finish/index.html
[70] http://www.youtube.com/watch?v=DvA9koAMXR8
[71] http://www.youtube.com/watch?v=3s7KG6QwUeQ
[72] Understanding the Robustness of SSDs under Power Fault, Zheng et al., 2013 — [discussion on HN]
[73] http://lkcl.net/reports/ssd_analysis.html — [discussion on HN]