04-原理篇:存储与IO系统(44-47)

168 阅读13分钟

44 | 理解IO_WAIT:I/O性能到底是怎么回事儿?

IO 性能、顺序访问和随机访问

每秒读写的次数,我们称之为IOPS,也就是每秒输入输出操作的次数。事实上,比起响应时间,我们更关注 IOPS 这个性能指标。IOPS 和 DTR(Data Transfer Rate,数据传输率)才是输入输出性能的核心指标。

如何定位 IO_WAIT?

top、iostat 以及 iotop

45 | 机械硬盘:Google早期用过的“黑科技”

拆解机械硬盘

image.png 一块机械硬盘是由盘面、磁头和悬臂三个部件组成的。

一个磁道,会分成一个一个扇区(Sector)。上下平行的一个一个盘面的相同扇区呢,叫作一个柱面(Cylinder)。

读取数据,其实就是两个步骤。一个步骤,就是把盘面旋转到某一个位置。在这个位置上, 我们的悬臂可以定位到整个盘面的某一个子区间。这个子区间的形状有点儿像一块披萨饼, 我们一般把这个区间叫作几何扇区(Geometrical Sector),意思是,在“几何位置上”,所有这些扇区都可以被悬臂访问到。另一个步骤,就是把我们的悬臂移动到特定磁道的特定扇区,也就在这个“几何扇区”里面,找到我们实际的扇区。找到之后,我们的磁头会落下,就可以读取到正对着扇区的数据。

一次硬盘上的随机访问,需要的时间由两个部分组成:

  1. 平均延时(Average Latency)。把盘面旋转,把几何扇区对准悬臂位置的时间。
  2. 平均寻道时间(Average Seek Time),也就是在盘面旋转之后,我们的悬臂定位到扇区的的时间。

Partial Stroking:根据场景提升性能

“缩短行程”技术
把所有数据都放在一个磁道上。比如,我们始终把磁头放在最外道的磁道上。这样,我们的寻道时间就基本为 0,访问时间就只有平均延时了。

46 | SSD硬盘(上):如何完成性能优化的KPI?

SSD 的读写原理

SSD 没有像机械硬盘那样的寻道过程,所以它的随机读写都更快。

image.png CPU Cache 用的 SRAM 是用一个电容来存放一个比特的数据。对于 SSD 硬盘,也可以先简单地认为,它是由一个电容加上一个电压计组合在一起,记录了一个或者多个比特。

SLC、MLC、TLC 和 QLC

能够记录一个比特很容易理解。给电容里面充上电有电压的时候就是 1,给电容放电里面没有电就是 0。采用这样方式存储数据的 SSD 硬盘,我们一般称之为使用了 SLC 的颗粒,全 称是 Single-Level Cell,也就是一个存储单元中只有一位数据。

但是,这样的方式会遇到和 CPU Cache 类似的问题,那就是,同样的面积下,能够存放下 的元器件是有限的。如果只用 SLC,我们就会遇到,存储容量上不去,并且价格下不来的 问题。于是呢,硬件工程师们就陆续发明了MLC(Multi-Level Cell)、TLC(Triple Level Cell)以及QLC(Quad-Level Cell),也就是能在一个电容里面存下 2 个、3 个乃 至 4 个比特。

只有一个电容,我们怎么能够表示更多的比特呢?别忘了,这里我们还有一个电压计。4 个 比特一共可以从 0000-1111 表示 16 个不同的数。那么,如果我们能往电容里面充电的时 候,充上 15 个不同的电压,并且我们电压计能够区分出这 15 个不同的电压。加上电容被放空代表的 0,就能够代表从 0000-1111 这样 4 个比特了。 不过,要想表示 15 个不同的电压,充电和读取的时候,对于精度的要求就会更高。这会导 致充电和读取的时候都更慢,所以 QLC 的 SSD 的读写速度,要比 SLC 的慢上好几倍。

P/E 擦写问题

SSD 硬盘的硬件构造,可以看到,大概是自顶向下是这么构成的。

image.png 其他的 I/O 设备一样,它有对应的接口和控制电路。现在的 SSD 硬盘用的是 SATA 或者 PCI Express 接口。在控制电路里,有一个很重要的模块,叫作FTL(Flash Translation Layer),也就是闪存转换层。这个可以说是 SSD 硬盘的一个核心模块,SSD 硬盘性能的好坏,很大程度上也取决于 FTL 的算法好不好。

实际 I/O 设备,其实和机械硬盘很像。现在新的大容量 SSD 硬盘都是 3D 封装 的了,也就是说,是由很多个裸片(Die)叠在一起的,就好像我们的机械硬盘把很多个盘面(Platter)叠放再一起一样,这样可以在同样的空间下放下更多的容量。

image.png

一张裸片上可以放多个平面(Plane),一般一个平面上的存储容量大概在 GB 级 别。一个平面上面,会划分成很多个块(Block),一般一个块(Block)的存储大小, 通 常几百 KB 到几 MB 大小。一个块里面,还会区分很多个页(Page),就和我们内存里面 的页一样,一个页的大小通常是 4KB。

在这一层一层的结构里面,处在最下面的两层块和页非常重要。

对于 SSD 硬盘来说,数据的写入叫作 Program。写入不能像机械硬盘一样,通过覆写(Overwrite)来进行的,而是要先去擦除(Erase),然后再写入。

SSD 的读取和写入的基本单位,不是一个比特(bit)或者一个字节(byte),而是一个(Page)。SSD 的擦除单位就更夸张了,我们不仅不能按照比特或者字节来擦除,连按照来擦除都不行,我们必须按照来擦除。

而且,你必须记住的一点是,SSD 的使用寿命,其实是每一个块(Block)的擦除的次数。你可以把 SSD 硬盘的一个平面看成是一张白纸。我们在上面写入数据,就好像用铅笔在白纸上写字。如果想要把已经写过字的地方写入新的数据,我们先要用橡皮把已经写好的字擦掉。但是,如果频繁擦同一个地方,那这个地方就会破掉,之后就没有办法再写字了。

SSD 读写的生命周期

image.png 一开始,所有块的每一个页都是白色的。随着我们开始往里面写数据,里面的有些页就变成了绿色。

然后,因为我们删除了硬盘上的一些文件,所以有些页变成了红色。但是这些红色的页,并不能再次写入数据。因为 SSD 硬盘不能单独擦除一个页,必须一次性擦除整个块,所以新的数据,我们只能往后面的白色的页里面写。这些散落在各个绿色空间里面的红色空洞,就好像硬盘碎片。

如果有哪一个块的数据一次性全部被标红了,那我们就可以把整个块进行擦除。它就又会变成白色,可以重新一页一页往里面写数据。这种情况其实也会经常发生。毕竟一个块不大,也就在几百 KB 到几 MB。你删除一个几 MB 的文件,数据又是连续存储的,自然会导致整个块可以被擦除。

随着硬盘里面的数据越来越多,红色空洞占的地方也会越来越多。于是,你会发现,我们就要没有白色的空页去写入数据了。这个时候,我们要做一次类似于 Windows 里面“磁盘碎片整理”或者 Java 里面的“内存垃圾回收”工作。找一个红色空洞最多的块,把里面的绿色数据,挪到另一个块里面去,然后把整个块擦除,变成白色,可以重新写入数据。

不过,这个“磁盘碎片整理”或者“内存垃圾回收”的工作,我们不能太主动、太频繁地去做。因为 SSD 的擦除次数是有限的。如果动不动就搞个磁盘碎片整理,那么我们的 SSD 硬盘很快就会报废了。

说到这里,你可能要问了,这是不是说,我们的 SSD 硬盘的容量是用不满的?因为我们总会遇到一些红色空洞?

image.png 一块 SSD 的硬盘容量,是没办法完全用满的。不过,为了不得罪消费者,生产 SSD 硬盘的厂商,其实是预留了一部分空间,专门用来做这个“磁盘碎片整理”工作的。一块标成 240G 的 SSD 硬盘,往往实际有 256G 的硬盘空间。SSD 硬盘通过我们的控制芯片电路,把多出来的硬盘空间,用来进行各种数据的闪转腾挪,让你能够写满那 240G 的空间。这个多出来的 16G 空间,叫作预留空间(Over Provisioning),一般 SSD 的硬盘的预留空间都在 7%-15% 左右。

SSD 硬盘,特别适合读多写少的应用。在日常应用里面,我们的系 统盘适合用 SSD。但是,如果我们用 SSD 做专门的下载盘,一直下载各种影音数据,然后 刻盘备份就不太好了,特别是现在 QLC 颗粒的 SSD,它只有几千次可擦写的寿命啊。 在数据中心里面,SSD 的应用场景也是适合读多写少的场景。我们拿 SSD 硬盘用来做数据 库,存放电商网站的商品信息很合适。但是,用来作为 Hadoop 这样的 Map-Reduce 应 用的数据盘就不行了。因为 Map-Reduce 任务会大量在任务中间向硬盘写入中间数据再删 除掉,这样用不了多久,SSD 硬盘的寿命就会到了。

47 | SSD硬盘(下):如何完成性能优化的KPI?

image.png 有一天,这些块的擦除次数到了,变成了坏块。但是,我们安装操作系统和软件的地方还没 有坏,而这块硬盘的可以用的容量却变小了。

磨损均衡、TRIM 和写入放大效应

FTL 和磨损均衡

磨损均衡(Wear-Leveling):让 SSD 硬盘各个块的擦除次数,均匀 分摊到各个块上。

实现这个技术的核心办法,和我们前面讲过的虚拟内存一样,就是添加一个间接层。这个间接层,就是我们上一讲给你卖的那个关子,就是 FTL 这个闪存转换层。

就像在管理内存的时候,我们通过一个页表映射虚拟内存页和物理页一样,在 FTL 里面,存放了逻辑块地址(Logical Block Address,简称 LBA)到物理块地址(Physical Block Address,简称 PBA)的映射。

操作系统访问的硬盘地址,其实都是逻辑地址。只有通过 FTL 转换之后,才会变成实际的物理地址,找到对应的块进行访问。操作系统本身,不需要去考虑块的磨损程度,只要和操作机械硬盘一样来读写数据就好了。

操作系统所有对于 SSD 硬盘的读写请求,都要经过 FTL。FTL 里面又有逻辑块对应的物理块,所以 FTL 能够记录下来,每个物理块被擦写的次数。如果一个物理块被擦写的次数多了,FTL 就可以将这个物理块,挪到一个擦写次数少的物理块上。但是,逻辑块不用变,操作系统也不需要知道这个变化。

TRIM 指令的支持

操作系统不去关心实际底层的硬件是什么,在 SSD 硬盘的使用上,也会带来一个问题。这个问题就是,操作系统的逻辑层和 SSD 的逻辑层里的块状态,是不匹配的。

我们在操作系统里面去删除一个文件,其实并没有真的在物理层面去删除这个文件,只是在文件系统里面,把对应的 inode 里面的元信息清理掉,这代表这个 inode 还可以继续使用,可以写入新的数据。这个时候,实际物理层面的对应的存储空间,在操作系统里面被标记成可以写入了。

所以,其实我们日常的文件删除,都只是一个操作系统层面的逻辑删除。这也是为什么,很多时候我们不小心删除了对应的文件,我们可以通过各种恢复软件,把数据找回来。同样的,这也是为什么,如果我们想要删除干净数据,需要用各种“文件粉碎”的功能才行。

这个删除的逻辑在机械硬盘层面没有问题,因为文件被标记成可以写入,后续的写入可以直接覆写这个位置。但是,在 SSD 硬盘上就不一样了。

image.png

使用 SSD 的硬盘情况下,你会发现,操作系统对于文件的删除,SSD 硬盘其实并不知道。这就导致,我们为了磨损均衡,很多时候在都在搬运很多已经删除了的数据。这就 会产生很多不必要的数据读写和擦除,既消耗了 SSD 的性能,也缩短了 SSD 的使用寿命。

现在的操作系统和 SSD 的主控芯片,都支持TRIM 命令。这个命令可以在文件被删除的时候,让操作系统去通知 SSD 硬盘,对应的逻辑块已经标记成已删除了。

写入放大

SSD 硬盘容易越用越慢。

当 SSD 硬盘的存储空间被占用得越来越多,每一次写入新数据,我们都可能没有足够的空白。我们可能不得不去进行垃圾回收,合并一些块里面的页,然后再擦除掉一些页,才能匀出一些空间来。

这个时候,从应用层或者操作系统层面来看,我们可能只是写入了一个 4KB 或者 4MB 的数据。但是,实际通过 FTL 之后,我们可能要去搬运 8MB、16MB 甚至更多的数据。

实际的闪存写入的数据量 / 系统通过 FTL 写入的数据量 = 写入放大”,可以得到,写入放大的倍数越多,意味着实际的 SSD 性能也就越差,会远远比不上实际 SSD 硬盘标称的指标。

而解决写入放大,需要我们在后台定时进行垃圾回收,在硬盘比较空闲的时候,就把搬运数据、擦除数据、留出空白的块的工作做完,而不是等实际数据写入的时候,再进行这样的操作。

AeroSpike:如何最大化 SSD 的使用效率?

总结

IOPS、IO_WAIT;机械硬盘、平均延迟+平均寻道时间;SSD(电容+电压计);磨损均衡、TRIM、写入放大;