You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

121 lines
12 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 23 | 存储案例如何降低SSD峰值延迟
你好,我是庄振运。
我们之前讲过,存储系统的性能很关键(参见[第17讲](https://time.geekbang.org/column/article/185154)。我们这一讲就探讨存储方面的优化案例是关于SSD性能的。
现在很多公司里面的高性能存储系统一般都是基于SSD的这主要归功于SSD价格在近几年的大幅度下降。但是SSD也不是包治百病的灵丹妙药也有自己的特殊性能问题。我们今天就重点讲述两点**SSD的损耗**和**IO访问延迟偶尔过大**的问题。
这里的第二个问题可能听起来很让人吃惊不是说SSD延迟很低吗
一般情况下是的。但是特殊情况下就不一定了这个就说来话长了它和SSD的内部原理有关。我们会一步步地探讨问题形成的原因和解决的策略。
## SSD为什么会损耗
我们的[第17讲](https://time.geekbang.org/column/article/185154)是关于存储系统的讲过SSD的工作原理和性能。为了防止你忘记我们就在这里快速地回顾一下其中的一个重要概念**写入放大**。
什么是写入放大呢当写入SSD的物理数据量大于应用程序打算写入SSD的逻辑数据量时就会造成“写入放大”。
如果是传统硬盘HDD就不会有写入放大的问题。那么SSD为什么会有写入放大呢这是因为SSD内部的工作原理和硬盘很不一样。
我们知道HDD是可以直接写入覆盖的。和HDD不同SSD里面的页面只能写入一次要重写的话必须先回收擦除而且只能在“块”这个级别进行擦除。因此呢SSD内部就需要不断地移动所存储的数据来清空需要回收的块。也就是说SSD内部需要进行块级别的“垃圾回收”。垃圾收集器必须有效地在SSD内部不断地回收块回收以前使用的页面空间然后才能在这个块上写入新数据。
因此对SSD的写入需求比对HDD的写入需求更高。
写入放大的缺点是什么?就是**会更快地损耗SSD的生命**。
每个SSD都有固定数量的擦除周期如果在很短时间内写到SSD太多数据就会导致SSD损耗太快有可能过早烧坏SSD。换句话说很高的写入速率可能会导致SSD在到达其预期使用寿命之前就发生故障。
所以我们要注意一个常用的指标叫年损耗率Burn Rate。这个指标是怎么定义的呢是用SSD的预期寿命推导出来的。比如一个SSD预期寿命是4年。那么每年可以损耗25%,这就是年损耗率。
## 如何减少SSD损耗
前面讲的“写入放大”,其实也可以用一个相应的具体指标来衡量,就是“**写入放大系数**”它代表物理写入SSD的数据与应用程序写入的逻辑数据之比。比如如果写入放大系数是2就表示写入每10KB的逻辑数据SSD实际上写了20KB。为了控制SSD的年损耗率我们需要尽量降低写入放大系数。
那么如何减少写入放大系数呢?常见的方法有两种:
1. 是**保留一定的空闲存储空间**这是因为写入放大系数是和SSD存储空闲率相关的。
2. 是**使用Trim**。
这两种方法可以同时使用,我们下面分别介绍。
我们先简单说一下第一种方法。每个SSD都有一定数量的预留空间这个空间不是SSD可用容量的一部分。这样做是有原因的。尽管我们可以使用工具来调整SSD卡上的可用容量但是我不建议你减少预配置的可用空间因为这将降低写入性能并可能大大缩减SSD的使用寿命。
我们在存储数据到SSD时候也不要存得太满也就是不要追求太高的空间使用率。那么我们将SSD可用存储容量的使用率目标定为多少比较合适呢一般来说我们可以定为80至85以保持较低的写入放大率。
**SSD的空闲可用空间越多内部垃圾收集的开销就越低就越有可能降低写入放大系数。**但是这种关系不是线性的,所以存在着收益递减的问题。
第二种方法是用Trim。我首先为你讲解一下什么是Trim。
Trim是个命令是操作系统发给SSD控制器的特殊命令。使用Trim命令操作系统可以通知SSD某些页面存储的数据不再有效了。比如对于文件删除操作操作系统会将文件的扇区标记为空闲以容纳新数据然后就可以将Trim命令发送到SSD。
Trim命令有什么好处呢
SSD收到Trim命令后SSD内部的控制器会更新其内部数据页面地图以便在写入新数据时不去保留无效页面。并且在垃圾回收期间不会复制无效页面这样就实现了更有效的垃圾收集也就减少了写操作和写入放大系数同时获得了更高的写吞吐量延长了驱动器的使用寿命。
Trim命令和机制虽然看起来很美好但是实际中会产生一些问题。原因在于不同的SSD厂商对Trim命令的处理方式以及具体的垃圾回收机制很不一样有的实现还不错有的就差强人意了因此Trim的性能在每种SSD那里会有所不同。我们后面会提到有些SSD的厂商的某些SSD因为对Trim的支持不太好会造成某些情况下性能非常差。
还要注意的是默认情况下操作系统一般不启用Trim。因此当文件系统删除文件时它只是将数据块标记为“未使用”。但是SSD控制器并不知道设备上的哪些页面可用因此无法真正释放设备上的无效空间。所以在没有启动Trim的情况下一旦SSD设备的可用容量填满即使文件系统知道设备上有可用容量SSD也会认为它自己已经存满。
那么怎么启动Trim呢要在SSD上启用连续Trim必须在mount这块SSD的时候使用“Discard”安装选项。如果一块SSD已经安装了想启动Trim那就需要卸载后重新安装“Discard”选项才能生效。也就是说使用remount命令是不起作用的。
所以对于单个系统而言最好在grub中启用mount选项并重新启动。
## 想减少SSD损耗却导致访问延迟过大
Trim的使用虽然带来了**降低SSD损耗**的好处,但也带来了一些坏处,特别是**IO访问可能延迟加大**的问题。
为什么Trim会影响应用程序性能呢
原因和SSD内部的实际机制有关。每个SSD内部都有一个FTLFlash Translation Layer映射表该表将操作系统的逻辑块地址LBALogical Block Address映射到SSD上的物理页面地址PPAPhysical Page Address。映射表在驱动器被写入时不断更新以后每个读取和写入IO都要引用。
一般来说映射表是存储在SSD驱动器的RAM中以便快速访问但是它的副本也存储在SSD中目的是在电源故障时能够保留LBA到PPA的映射。随着SSD上面内容和数据的不断变化这些变化包括新写入IO或垃圾回收RAM中的映射表也不断更新并且持续写入SSD中。
如果在文件系统上启用了Discard选项那么每次删除文件时都会生成Trim命令。因为每次Trim都会更改映射表所以对映射表的更改也就实际地记录到SSD中。这项操作可能需要花费比较长的时间比如几毫秒的时间才能完成在这个更改过程中普通的数据读取和写入I /O会阻塞并且阻塞到所有的映射表调整都被完全处理为止。当今业界的大多数SSD都是这样工作的。
上面我们看到由于Trim的处理会阻塞普通的数据读取和写入I /O直到Trim完成映射表记录才返回所以Trim的延迟对普通读写I O的延迟具有重大影响尤其对高分位数比如P99、99.9)的读写IO延迟影响更大。减少Trim延迟就是减少IO延迟。所以我们需要尽量减少Trim的等待处理时间。
另外值得你注意的是每个SSD厂商和每款SSD对Trim的具体处理方式都可能不同颇有些厂商的某些SSD具有严重的问题。我们生产实践中碰到过好几种这样的SSD比如有厂商的一种SSD在大量删除数据时有很大的延迟。这就要求我们在选购SSD时候要特别小心尤其是要做彻底的性能测试。
## 如何避免Trim带来的延迟
我们刚才讲了用Trim的好处和坏处。好处是可以减少SSD的损耗延长SSD的寿命坏处是会造成应用程序的IO读写延迟变大。
那么怎么才能尽量避免Trim带来的坏处呢我们这里谈两种方式一是对Discard选项本身的调优二是使用[fstrim](http://man7.org/linux/man-pages/man8/fstrim.8.html)命令。这两种方式分别对应使用Discard被启用和不被启用的两种情况。
第一种方式是在启用了Discard后对Discard的调优。对于已经启用Discard的场景下Trim命令默认是没有大小限制也就是说一次发送会尽可能多的删除命令。但是如果一次删除的数据太多SSD可能需要很长的时间才能返回其他读写IO就会感受到很大的延迟。
那么我们就可以微调了这里我们就可以借助另外一个参数discard\_max\_bytes对Discard进行调优。这个参数是一个操作系统内核参数从名字也听得出它可以指定一次Trim的最大数据量。
调整这个参数的优点是可以根据实际可接受IO延迟的需要来随意微调。举个例子假如可接受IO延迟比较大那就可以设置一个较大的discard\_max\_byes数值比如2GB。使用这个参数的坏处是当有大文件删除时如果没有相应的重新调整参数Trim的吞吐量会受影响。
第二种方式是在没有启用Discard的场景下采用fstrim来调优。fstrim也是一个命令它可以控制Trim来删除掉SSD上的文件系统不再使用的数据。默认情况下fstrim将删除文件系统中所有未使用的块但是这个命令有其他的选项根据删除范围或大小来进行微调。
这个命令一般用于Discard没有被启用的场景下。为了达到最好的效果都是周期性的或者采用外部事件触发来运行这个命令比如用Cron来每天固定时间运行或者每当SSD存储使用率到了某个大小就运行。
下图展示了一个实际生产环境中的性能数据。
![](https://static001.geekbang.org/resource/image/00/76/0032afc607be8d57a6b049bd541a6576.png)
这是一个采用fstrim而降低IO延迟的例子。横轴是时间纵轴是对SSD进行读操作的IO延迟。红色箭头是运行fstrim的时间。我们可以看到在fstrim后IO延迟大幅度地降低了。
采用fstrim这个方式的优点是可以根据实际需要来决定何时运行并且更好地微调和控制Trim的工作。
这个方式也有缺点就是如果不够小心运行这个命令时可能导致很长时间的SSD读写挂起阻塞在这个阻塞的过程中SSD完全没有响应不能读写。我见过几次这样的生产例子阻塞了好几分钟甚至几个小时的时间整个SSD完全不能写入和读取数据。
## 总结
SSD不断地重写会损坏其存储能力就如同一口宝刀不断地征战砍伐后也会有缺口。这让我想起了唐代诗人马戴写的一首气势磅礴的《出塞词》“金带连环束战袍马头冲雪度临洮。卷旗夜劫单于帐乱斫胡兵缺宝刀。”
![](https://static001.geekbang.org/resource/image/97/b0/973542a2647ce99fb76e06a2416560b0.png)
为了延长SSD的寿命我们可以采用Trim方式以去除不必要的内部重写。
但是这种方式在某些特殊情况下会增大外部IO的访问延迟。解决这一问题的方法是对Trim进行调优。我们这一讲就集中探讨了几种调优解决方案来解决这一特殊情况下的问题。
## 思考题
你们公司部署SSD了吗有没有遇到关于损耗过大的问题以及Trim的问题和相关讨论最后采取的解决方案是什么呢
欢迎你在留言区分享自己的思考,与我和其他同学一起讨论,也欢迎你把文章分享给自己的朋友。