gitbook/系统性能调优必知必会/docs/257091.md
2022-09-03 22:05:03 +08:00

116 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 大咖助场2庄振运与程序员相关的SSD性能知识
你好,我是庄振运。我是[《性能工程高手课》](https://time.geekbang.org/column/intro/100041101)的专栏作者很荣幸受邀来到陶辉老师的专栏做一期分享。今天我们来讲一点SSD相关的性能知识。SSDSolid State Drive是硬盘的一种有时候也叫Flash或者固态硬盘。
最近几年SSD的发展和演化非常迅速。随着市场规模的增大和技术的进步SSD的价格也大幅度降低了。在很多实时的后台系统中SSD几乎已经成了标准配置了。所以了解它的机制和性能对你的工作会很有益处的。
相对于传统硬盘HDDHard Disk DriveSSD有完全不同的内部工作原理和全新的特性。有些机制不太容易理解而且根据你工作的领域需要理解的深度也不一样。所以我把这节课的内容按照由浅入深的原则分成了三个层次。
第一个层次是关注SSD的外部性能指标第二个层次是了解它的内部工作机制第三个层次是设计对SSD友好的应用程序。
## 比HDD更快的硬盘
很多人对传统硬盘了解较多毕竟这种硬盘在业界用了好几十年了很多教科书里面都讲述过。所以对SSD的性能我先用对比的方式带你看看它们的外部性能指标和特性。
一个硬盘的性能最主要体现在这三个指标IOPS带宽/吞吐率和访问延迟。**IOPS** (Input/Output Per Second) ,即每秒钟系统能处理的读写请求数量。**访问延迟**指的是从发起IO请求到存储系统把IO处理完成的时间间隔。**吞吐率**Throughput或者带宽Bandwidth衡量的是实际数据传输速率。
对于传统硬盘我们应该比较熟悉它的内部是如何操作的简单来说当应用程序发出硬盘IO请求后这个请求会进入硬盘的IO队列。当轮到这个IO来存取数据时磁头需要机械运动到数据存放的位置这就需要磁头寻址到相应的磁道和旋转到相应的扇区然后才是数据的传输。对于一块普通硬盘而言随机IO读写延迟就是8毫秒左右IO带宽大约每秒100MB而随机IOPS一般是100左右。
SSD的种类很多按照技术来说有单层和多层。按照质量和性能来分有企业级和普通级。根据安装的接口和协议来分有SAS, SATA, PCIe和NVMe等。
我用一张表格来对比一下HDD和SSD的三大性能指标的差异。这里考虑比较流行的NVMe协议的SSD。你可以看到SSD的随机IO延迟比传统硬盘快百倍以上一般在微妙级别IO带宽也高很多倍可以达到每秒几个GB随机IOPS更是快了上千倍可以达到几十万。
![](https://static001.geekbang.org/resource/image/7c/04/7c0d537e9334e1d622d40f28168e1c04.jpg)
## SSD的性能特性和机制
SSD的内部工作方式和HDD大相径庭我们先了解几个概念。
**单元Cell**、**页面Page**、**块Block**。当今的主流SSD是基于NAND的它将数字位存储在单元中。每个SSD单元可以存储一位或多位。对单元的每次擦除都会降低单元的寿命所以单元只能承受一定数量的擦除。单元存储的位数越多制造成本就越少SSD的容量也就越大但是耐久性擦除次数也会降低。
一个页面包括很多单元典型的页面大小是4KB页面也是要读写的最小存储单元。SSD上没有“重写”操作不像HDD可以直接对任何字节重写覆盖。一个页面一旦写入内容后就不能进行部分重写必须和其它相邻页面一起被整体擦除重置。
多个页面组合成块。一个块的典型大小为512KB或1MB也就是大约128或256页。块是擦除的基本单位每次擦除都是整个块内的所有页面都被重置。
了解完以上几个基础概念,我们重点看看**IO和垃圾回收(Garbage Collection)** 。对SSD的IO共有三种类型读取、写入和擦除。读取和写入以页为单位。IO写入的延迟具体取决于磁盘的历史状态因为如果SSD已经存储了许多数据那么对页的写入就经常需要移动已有的数据。一般的读写延迟都很低在微秒级别远远低于HDD。擦除是以块为单位的。擦除速度相对很慢通常为几毫秒。所以对同步的IO发出IO的应用程序可能会因为块的擦除而经历很大的写入延迟。为了尽量地减少这样的场景保持空闲块的阈值对于快速的写响应是很有必要的。SSD的垃圾回收GC的目的就在于此。GC可以回收用过的块这样可以确保以后的页写入可以快速分配到一个全新的页。
![](https://static001.geekbang.org/resource/image/4b/70/4b3705248ccb0144516a77849c17dd70.png)
**写入放大Write Amplification, or WA)。** 这是SSD相对于HDD的一个缺点即实际写入SSD的物理数据量有可能是应用层写入数据量的多倍。一方面页级别的写入需要移动已有的数据来腾空页面。另一方面GC的操作也会移动用户数据来进行块级别的擦除。所以对SSD真正的写操作的数据可能比实际写的数据量大这就是写入放大。一块SSD只能进行有限的擦除次数也称为编程/擦除P/E周期所以写入放大效用会缩短SSD的寿命。
**耗损平衡(Wear Leveling) 。**对每一个块而言一旦达到最大数量该块就会死亡。对于SLC块P/E周期的典型数目是十万次对于MLC块P/E周期的数目是一万而对于TLC块则可能是几千。为了确保SSD的容量和性能我们需要在擦除次数上保持平衡SSD控制器具有这种“耗损平衡”机制可以实现这一目标。在损耗平衡期间数据在各个块之间移动以实现均衡的损耗这种机制也会对前面讲的写入放大推波助澜。
## 设计对SSD友好的程序
SSD的IO性能相对于HDD来说IOPS和访问延迟提升了上千倍吞吐率也是几十倍但是SSD的缺点也很明显有三个贵、容量小、易损耗。随着技术的发展这三个缺点近几年在弱化。
现在越来越多的系统采用SSD来减轻应用程序的IO性能瓶颈。许多部署的结果显示与HDD相比SSD带来了极大的应用程序的性能提升。但是在大多数部署方案中SSD仅被视为“更快的HDD”SSD的潜力并未得到充分利用。尽管使用SSD作为存储时应用程序可以获得更好的性能但是这些收益主要归因于SSD提供的更高的IOPS和带宽。
进一步讲如果应用程序的设计充分考虑了SSD的内部机制从而设计为对SSD友好则可以更大程度地优化SSD从而进一步提高应用程序性能也可以延长SSD的寿命而降低运用成本。接下来我们就看看如何在应用程序层进行一系列SSD友好的设计更改。
### 为什么要设计SSD友好的软件和应用程序
SSD友好的程序可以获得三种好处
* 提升应用程序性能;
* 提高SSD的 IO效率
* 延长SSD的寿命。
我分别说明一下。
**更好的应用程序性能。**尽管从HDD迁移到SSD通常意味着更好的应用程序性能这主要是得益于SSD的IO性能更好但在不更改应用程序设计的情况下简单地采用SSD可能无法获得最佳性能。我们曾经有一个应用程序就是如此。该应用程序需要不断写入文件以保存数据主要性能瓶颈就是硬盘IO。使用HDD时最大应用程序吞吐量为每秒142个查询QPS。无论对应用程序设计进行各种更改还是调优这都是可以获得的最好性能。
当迁移到具有相同应用程序的SSD时吞吐量提高到2万QPS速度提高了140倍。这主要来自SSD提供的更高IOPS。在对应用程序设计进行进一步优化使其对SSD友好之后吞吐量提高到10万QPS与原来的简单设计相比提高了4倍。
这其中的秘密就是使用多个并发线程来执行IO这就利用了SSD的内部并行性。记住多个IO线程对HDD毫无益处因为HDD只有一个磁头。
**更高效的存储IO。** SSD上的最小内部IO单元是一页比如4KB大小。因此对SSD的单字节读/写必须在页面级进行。应用程序对SSD的写操作可能会导致对SSD上的物理写操作变大这就是“写放大WA”。因为有这个特性如果应用程序的数据结构或IO对SSD不友好就会让写放大效果无谓的更大导致SSD的IO不能被充分利用。
**更长的使用寿命。**SSD会磨损因为每个存储单元只能维持有限数量的写入擦除周期。实际上SSD的寿命取决于四个因素SSD大小、最大擦除周期数、写入放大系数和应用程序写入速率。例如假设有一个1TB大小的SSD一个写入速度为100MB每秒的应用程序和一个擦除周期数为1万的SSD。当写入放大倍数为4时SSD仅可持续10个月。具有3千个擦除周期和写入放大系数为10的SSD只能使用一个月。鉴于SSD相对较高的成本我们很希望这些应用程序对SSD友好从而延长SSD的使用寿命。
### SSD友好的设计原则
在设计程序的时候我们可以把程序设计成对SSD友好以获得前面提到的三种好处。那有哪些对SSD友好的程序设计呢我这里总结了四个原则大体上分为两类数据结构和IO处理。
**1.数据结构:避免就地更新的优化**
传统HDD的寻址延迟很大因此使用HDD的应用程序通常经过优化以执行不需要寻址的就地更新比如只在一个文件后面写入。如下图所示执行随机更新时吞吐量一般只能达到约170QPS而对于同一个HDD就地更新可以达到280QPS远高于随机更新如下左图所示
![](https://static001.geekbang.org/resource/image/42/fb/42339220b197186548330ayy0b08d9fb.jpg)
在设计与SSD配合使用的应用程序时这些考虑就不再有效了。对SSD而言随机读写和顺序读写性能类似就地更新不会获得任何IOPS优势。此外就地更新实际上会导致SSD性能下降。原因是包含数据的SSD页面无法直接重写因此在更新存储的数据时必须先将相应的SSD页面读入SSD缓冲区然后将数据写入干净页面。 SSD中的“读取-修改-写入”过程与HDD上的直接“仅写入”行为形成鲜明对比。相比之下SSD上的随机更新就不会引起读取和修改步骤即仅仅“写入”因此速度更快。使用SSD以上相同的应用程序可以通过随机更新或就地更新来达到大约2万QPS如上右图所示
**2.数据结构:将热数据与冷数据分开**
对于几乎所有处理存储的应用程序磁盘上存储的数据的访问概率均不相同。我们考虑这样一个需要跟踪用户活动的社交网络应用程序对于用户数据存储简单的解决方案是基于用户属性例如注册时间将所有用户压缩在同一位置例如某个SSD上的文件以后需要更新热门用户的活动时SSD需要在页面级别进行访问即读取/修改/写入。因此如果用户的数据大小小于一页则附近的用户数据也将一起访问。如果应用程序其实并不需要附近用户的数据则额外的数据不仅会浪费IO带宽而且会不必要地磨损SSD。
为了缓解这种性能问题在将SSD用作存储设备时应将热数据与冷数据分开。以不同级别或不同方式来进行分隔例如存到不同的文件文件的不同部分或不同的表。
**3\. IO处理避免长而繁重的写入**
SSD通常具有GC机制不断地回收存储块以供以后使用。 GC可以后台或前台方式工作。
SSD控制器通常保持一个空闲块的阈值。每当可用块数下降到阈值以下时后台GC就会启动。由于后台GC是异步发生的即非阻塞因此它不会影响应用程序的IO延迟但是如果块的请求速率超过了GC速率并且后台GC无法跟上则将触发前台GC。
在前台GC期间必须即时擦除即阻塞每个块以供应用程序使用这时发出写操作的应用程序所经历的写延迟会受到影响。具体来说释放块的前台GC操作可能会花费数毫秒以上的时间从而导致较大的应用程序IO延迟。出于这个原因最好避免进行长时间的大量写入操作以免永远不使用前台GC。
**4\. IO处理避免SSD存储太满**
SSD磁盘存储太满会影响写入放大系数和GC导致的写入性能。在GC期间需要擦除块以创建空闲块。擦除块前需要移动并保留有效数据才能获得空闲块。有时为了获得一个空闲块我们需要压缩好几个存储块。每个空闲块的生产需要压缩的块数取决于磁盘的空间使用率。
假设磁盘满百分比平均为A要释放一块则需要压缩1 /1-A块。显然SSD的空间使用率越高将需要移动更多的块以释放一个块这将占用更多的资源并导致更长的IO等待时间。例如如果A=80则大约移动五个数据块以释放一个块当A=95将移动约20个块。
## 总结
各种存储系统的基础是传统硬盘或者固态硬盘固态硬盘SSD的IO性能比传统硬盘高很多。如果系统对IOPS或者延迟要求很高一般都采用SSD。
现在已经有很多专门针对SSD来设计的文件系统、数据库系统和数据基础架构它们的性能比使用HDD的系统都有了很大的提升。
SSD有不同于HDD工作原理所以在进行应用程序设计的时候如果可以做到对SSD友好那么就可以充分发挥SSD的全部性能潜能应用程序的性能会进一步提高。你可以参考我们今天总结的四个设计原则进行实践。
## 思考题
最后给你留几道思考题。你们公司里面有哪些后台服务和应用是使用SSD作为存储的而对这些使用SSD的系统有没有充分考虑SSD的特性做深层的优化呢比如降低损耗
感谢阅读,如果今天的内容让你有所收获,欢迎把它分享给你的朋友。