Skip to content

Latest commit

 

History

History
60 lines (47 loc) · 7.1 KB

顺序IO.md

File metadata and controls

60 lines (47 loc) · 7.1 KB

磁盘原理

磁盘是计算机组成一个重要部分,也是最主要的存储部件。我们知道内存也可以用于存储数据,而且读写速度非常快,通常比磁盘要快几个数量级,但是内存资源是珍贵而且有限的,通常我们的服务器内存也就是16G,32G,而需要存储的数据动不动就是几百G以上,很明显不能把数据都存储到内存,而且内存的数据是暂时的,当计算机重启就会丢失,不适合存储需要持久化的数据。所以基于磁盘的文件存储系统是非常重要和常用的,我们平时使用的数据库、mq消息存储、程序日志等都是存储在磁盘上。计算机工作过程中对磁盘的读写是非常频繁的,这也是我们常说的磁盘IO,而读写主要有随机读写和顺序读写两种方式,这两者有很大的性能差异。接下来我们先看下磁盘的工作原理。

组成部件
image
如图是磁盘的物理结构,主要部件有磁盘盘片,传动手臂,读写磁头和主轴马达。磁盘盘片是数据实际存储的位置,读写就是通过主轴马达让磁盘盘片转动,然后转动传动手臂上的读写磁头在磁盘盘片上进行读写操作。

磁道和扇区
image
上图显示的是一个盘片,盘片中一圈圈灰色同心圆为一条条磁道,从圆心向外画直线,可以将磁道划分为若干个弧段,每个磁道上一个弧段被称之为一个扇区(图践绿色部分)。

磁盘的读写主要有三个步骤:

1.寻道
指将读写磁头移动到正确的磁道上所需要的时间,目前磁盘的平均寻道时间约为3-15ms。
2.旋转延迟
指盘片旋转将请求数据所在的扇区移动到读写磁头下方所需要的时间。旋转延迟主要取决于磁盘转速,如7200rpm的磁盘平均旋转延迟大约为4.17ms,转速为15000rpm的磁盘的平均旋转延迟为2ms。
3.数据传输时间
指传输读写数据需要的时间,它主要取决于数据传输率。目前IDE/ATA能达到133MB/s,SATA II可达到300MB/s的接口数据传输率,数据传输时间通常远小于寻道时间和旋转延迟时间,计算时一般可以忽略。

通过上面的描述我们知道磁盘的读写性能消耗主要花在寻道和旋转延迟上,如果能减少这两个步骤所消耗的时间,读写速度将大大提升。

随机IO和顺序IO

PageCache页缓存,是操作系统为了提升磁盘的读写效率,将一部分内存用来加速文件读写的区域,如图buffer/cache所占的空间部分就是PageCache所使用(还有部分是BufferCache使用)。
image
PageCache是操作系统用于缓存磁盘上的部分数据的内存区域,如果我们请求的数据正好在PageCache上,那么可以直接返回,免去了对磁盘的IO操作。

预读和回写
预读是指操作系统在将磁盘数据加载到内存中时,通常会将连续的后面几个页的数据也一起加载出来,这样如果后期读取的数据在这些页中,就可以直接从缓存读取,不需要再次从磁盘加载。这个是出于一个原则:当读取一个数据时,很可能会对后续连续的数据进行读取。这个和mysql是类似的,尽管我们只读取一条数据,mysql也会加载一页的数据出来。
回写是将数据写入到PageCache,而不是直接写入磁盘,这和普通的内存写入效率是一样的。而PageCache的内容会由操作系统的pdflush异步线程在根据一定的策略在合适的时间同步到磁盘,pdflush后面版本改成了效率更高的bdi_writeback机制。
可以看到使用PageCache是在程序和磁盘间使用内存做了一层中介,提升性能。但这并不是没有代价的,最直接的就是它占用了我们的实际内存,也就是上图的free空间,当实际内存不足时,就需要让操作系统释放buffer/cache,毕竟这个是借用的。而写入PageCache也有丢失数据的风险,因为它是由操作系统异步写入磁盘。

顺序IO就是利用了PageCache实现的,由于大大降低了寻道和旋转延迟时间,其速度几乎接近于内存的读写速度。而随机IO由于需要不断的进行寻道和旋转,效率很低。SSD在设计上对寻道和旋转延迟上做了优化,随机读写效率有了很大提升,但是我们需要知道顺序读写始终有它的优势,可以想象我们再一张布满格子的纸上面写字,连续写的速度比随便挑格子写的速度要快得多,省去找格子和移动笔的时间。

kafka/rocketmq

kafka和rocketmq存储消息都是持久化到磁盘上,它们就是利用磁盘的顺序IO来处理消息的读写,保持高性能。
我们可以看下kafka官网的介绍
image
标题就是:别担心文件系统的性能问题
翻译过来的主要内容就是:
1.文件系统的读写效率可以比想象中快得多
2.操作系统对顺序读写做了优化,其速度可以达到随机写入的6000倍,甚至比内存的随机读写还快
kafka对消息的读写使用的是FileChannel类(index使用的是mmap)

rocketmq的设计参考了kafka,也使用顺序写对消息进行持久化
image
源码:
image
rocketmq使用java开发,主要是使用MappedByteBuffer对文件进行读写,它使用mmap的方式对地址进行映射,以减少数据拷贝的次数,这点我们在零复制有所介绍。
rocketmq默认使用MappedByteBuffer,而不是像kafka使用FileChannel。FileChannel并非直接写入PageCache,而是先写入内存,再写到PageCache,其force()方法可以通知系统把数据刷到磁盘。
FileChannel相比MappedByteBuffer多了一层写内存的动作,但FileChannel是基于block的操作,在数据比较大时仍然有优势(通常是4kb的整数倍时)。所以两者没有哪个是文件IO的银弹,可以简单认为前者在写入小数据到文件时具有一定优势,后者对数据大小比较大时基于块的操作效率比较好,通常我们的mq传输的数据应该尽可能小。

Active mq在社区的反响平平,原因就是它读写都是使用RandomAccessFile随机读写的方式,效率较低。

参考

rocketmq while not use FileChannel.write
MappedByteBuffer VS FileChannel ,孰强孰弱?
磁盘io那些事
文件 IO 操作的一些最佳实践