我们大部分时间都是在开发应用系统,当我们的功能实现时和实现后,我们可能会经常的思考或者讨论关于性能方面的问题,性能优化也有很多个方面,那么我们今天主要来一起探讨一下IO性能。
谈到IO性能,我们就会联想到我们电脑的一个非常重要的组件 ---- 硬盘
我们现在常见的硬盘种类有两种 机械硬盘 和 固态硬盘,也有人叫 HDD硬盘(全称HardDiskDrive)和SSD(全称SolidStateDrive)硬盘
我们在衡量web服务性能时应该都大概率的都听说过两个词,吞吐率和响应时间。
我们在看硬盘厂商的性能报告中,我们比较关注两个比较重要的指标数据传速率 和 响应时间。
我们先来直观的感受一下这两个参数
这是一个SATA3.0接口的SSD硬盘实测结果
这是一个PCI-E接口的SSD硬盘实测结果 2G/s
消费级市场中SATA 3.0是最为常见的固态硬盘接口
准确来说应该叫SATA Revision 3.0(简称SATA3.0或SATA 6Gbps)它代表所使用的是SATA Revision 3.0标准,速度更快,相对于SATA Revision 2.0。SATA是硬盘接口的标准规范,事实上,SATA 6Gbps接口是一个不太规范的说法,正确说法是SATA III(SATA3.0),接口速度是6Gbps。当然,大家都习惯这么说。这是市面上最常见的机械硬盘/固态硬盘接口,大多数人买的硬盘都是在用这种接口。
SATA3.0的理论传输速率应该是 6Gbps = 6 * 1024 / 8 = 768M/s, 从图中可以看到实测会比理论低一些。
铺垫了这么多,让我们大家从感性上理解一下,我们接下来我们从理性上在理解一下,一起算个题
IO性能的核心指标
HDD硬盘的接口主要是SATA3.0,传输率大概是 200M/s,
以一个7200转的HDD硬盘为例,我们来简单的计算一下:
它的传输速率是 200M/s,响应时间按几毫秒来算。
我们平时往数据库里插入一条记录差不多1KB左右的大小,200M / 1KB = 200 * 1024 / 1 = 204800/s 大概 20万条, 也就是说一秒钟可以插入 20w条数据,这性能看起来还是挺不错的。
但是这个结果似乎与我们平时经验并不符合?这又是为什么呢?
答案来自于硬盘的读写,在顺序读写和随机读写的情况下,硬盘的性能是完全不同的。
上面的两个图中有一个“4K”的指标,这个指标的意思就是程序随机读取磁盘上4K大小的数据,一秒能读取多少的数据。
这里我们引入一个概念 IOPS,我们一起看下维基百科的解释
这里自己简单的总结一下就是 每秒输入输出操作次数.
和响应时间比起来,其实我们应该真正关心的是 IOPS和DTR(Data Transfer Rate 数据传输率)
这两个才是输入输出性能的核心指标。
上面的问题说到这里我们大概心中也有了答案,就是我们在实际的软件开发中,对于数据的读写更多的是随机读写,而不是顺序读写,我们平时讨论的“并发”,就是很多个不同的进程和请求来访问服务器,它们所访问的数据就很难是顺序读写,这种情况下,随机读写的IOPS就是性能的核心指标了。
那一块HDD硬盘IOPS大概是100左右,而不是20万次。
接下来我们就来讨论一下,一块HDD硬盘的IOPS是怎么来的。我们就需要先了解一下机械硬盘的构造。
https://www.youtube.com/watch?v=SkGc1RxMOI4
我们大概了解了机械硬盘的基本构造
平均延时
7200RPM(Rotations Per Minute)每分钟旋转圈数 等价于 一秒钟 120圈
盘面旋转,把几何扇区对准悬臂的时间,这个时间跟硬盘的转速相关,随机情况下,平均找到一个几何扇区,大概需要旋转半圈盘面,7200 / 60 * 2 = 240半圈,那么一秒内旋转240半圈,换算成ms 1000ms / 240 = 4.17ms
除了这个平均延时,还有一个延时
平均寻道时间(Average Seek Time)
我们的悬臂定位到扇区的的时间。我们现在用的 HDD 硬盘的平均寻道时间一般在 4-10ms
知道了这两个延时时间
那么在一个硬盘上随机找一个数据至少要花费大概 (4 + 4.17) 到 (10 + 4.17) 大概 8到14ms
因为机械硬盘没有办法并发的进行定位和读取数据,所以以一个7200转的硬盘一秒钟能够读写的次数就是
1s / 8ms = 1000ms/8ms = 125次 1s / 14ms = 1000ms/14ms = 71.4次
这样我们就计算出来了一块7200转的机械硬盘的IOPS,一块7200转的硬盘的IOPS大概是在 71 到 125 约等于 100左右
数据传输时间
数据传输时间是指完成传输所请求的数据所需要的时间,它取决于数据传输率,其值等于数据大小除以数据传输率。
SSD硬盘(固态硬盘)
SSD主要由主控制器,存储单元,缓存(可选),以及跟HOST接口(诸如SATA,SAS, PCIe等)组成
一个SATA3.0接口的SSD硬盘的传输率大概是 768M/s
这是一个SSD硬盘的实际测试结果,大概500M/s
可以把SSD硬盘的接口换成 PCI-Express,速度还能在进一步的提升, 大概到了 2G/s的传输速率
从测试结果来看,SATA3.0和PCI-Express接口的硬盘性能差异变的很小了,这是因为接口本身的速度已经不是硬盘访问速度的瓶颈了,并且更重要的是即使是PCI-Express接口硬盘,随机读取的性能也只有 40M/s。是顺序读取情况下的几十分之一。
我们来计算一下SSD硬盘的IOPS
40M/s 读取 4KB
40 * 1024 / 4 = 10240次
也就是说一秒之内,这块硬盘可以随机读取10000次4KB数据
写入就是 90 * 1024 / 4 = 23040次,写入的话会多一些
从上面我们简单的总结一下
SSD之所以比HDD快,就是因为SSD内部不存在任何可移动的机械结构,在工作时,数据通过接口进入主控器,经处理后再分配到闪存芯片中存储,不论是读取或者写入,都是以纯电子电路的方式实现,几乎没有任何延时。
固态硬盘看起来非常不错,但是固态硬盘本身也自己的一些特性,
擦写问题
固态硬盘的数据写入不像机械硬盘那样通过覆写来进行的,而是先进行擦除然后再写入。
SSD 的读取和写入的基本单位,不是一个比特(bit)或者一个字节(byte),而是一个页(Page)。SSD 的擦除单位就更夸张了,我们不仅不能按照比特或者字节来擦除,连按照页来擦除都不行,我们必须按照块来擦除。
SSD 的使用寿命,其实是每一个块(Block)的擦除的次数,
FTL和磨损均衡
如果总是频繁的进行擦写一个块,这个块很快就会成为坏块,那有没有什么办法能能够让各个块的擦写次数均匀一些呢? 这个策略就是 磨损均衡, 类似与web应用到负载均衡, web可是使用Ngnix, 固态硬盘中也是增加来一个层叫 FTL(闪存转换层)
在 FTL 里面,存放了逻辑块地址(Logical Block Address,简称 LBA)到物理块地址(Physical Block Address,简称 PBA)的映射。
操作系统访问的硬盘地址,其实都是逻辑地址。只有通过 FTL 转换之后,才会变成实际的物理地址,找到对应的块进行访问。操作系统本身,不需要去考虑块的磨损程度,只要和操作机械硬盘一样来读写数据就好了。
TRIM
但是,操作系统不去关心实际底层的硬件是什么,在 SSD 硬盘的使用上,也会带来一个问题。这个问题就是,操作系统的逻辑层和 SSD 的逻辑层里的块状态,是不匹配的。我们在操作系统里面去删除一个文件,其实并没有真的在物理层面去删除这个文件,只是在文件系统里面,把对应的 inode 里面的元信息清理掉,这代表这个 inode 还可以继续使用,可以写入新的数据。这个时候,实际物理层面的对应的存储空间,在操作系统里面被标记成可以写入了。
这个删除的逻辑在机械硬盘层面没有问题,因为文件被标记成可以写入,后续的写入可以直接覆写这个位置。
操作系统只有在在刚刚的inode里面在写入新的数据的时候,SSD硬盘才能之前存储的文件是应该被删除的。才能去把之前的块标记称删除
在使用 SSD 的硬盘情况下,你会发现,操作系统对于文件的删除,SSD 硬盘其实并不知道。这就导致,我们为了磨损均衡,很多时候在都在搬运很多已经删除了的数据。这就会产生很多不必要的数据读写和擦除,既消耗了 SSD 的性能,也缩短了 SSD 的使用寿命。
为了解决这个问题,现在的操作系统和 SSD 的主控芯片,都支持 TRIM 命令。这个命令可以在文件被删除的时候,让操作系统去通知 SSD 硬盘,对应的逻辑块已经标记成已删除了。
写放大效应
随着HOST的持续写入,FLASH存储空间慢慢变小,直到耗尽。如果不及时清除这些垃圾数据,HOST就无法写入。SSD内部都有垃圾回收机制,它的基本原理是把几个Block中的有效数据(非垃圾数据,上图中的绿色小方块表示的)集中搬到一个新的Block上面去,然后再把这几个Block擦除掉,这样就产生新的可用Block了。这个时候,从应用层或者操作系统层面来看,我们可能只是写入了一个 4KB 或者 4MB 的数据。但是,实际通过 FTL 之后,我们可能要去搬运 8MB、16MB 甚至更多的数据。
这就导致了写入放大效应 16MB / 4MB = 4 这个数值越大意味着实际的 SSD 性能也就越差,会远远比不上实际 SSD 硬盘标称的指标。
而解决写入放大,需要我们在后台定时进行垃圾回收,在硬盘比较空闲的时候,就把搬运数据、擦除数据、留出空白的块的工作做完,而不是等实际数据写入的时候,再进行这样的操作。
附录
我觉得没有必要使用SSD,Kafka主要利用PageCache来提高系统写入的性能,而且Kafka对磁盘大多是顺序读写,在磁盘上提高IOPS,并不能显著的提升Kafka的性能。
参考资料
固态硬盘
IOPING命令
IO排查
扩展知识
Aerospike
www.slideshare.net/AerospikeDB…
AeroSpike据我所知在国内外应用都很普遍了。没有Redis火的核心原因我觉得是因为开源得晚了。另外,就是对于大部分数据量没有那么大的创业公司,用内存作为缓存,存储空间也就够了,那用Redis也就足够了,暂时还用不上AeroSpike
4K-64Thrd
4K-64Thrd是随机64队列深度测试,软件则会生成64个16MB大小的测试文件(共计1GB),然后同时以4KB的单位尺寸,同时在这64个文件中进行写入和读取测试,最后依然以平均成绩为结果