现在存储数据的方式有很多种,而硬盘由于其价格和数据保护方面的优势成为了大多数用户的首选。但是硬盘和内存相比,IO读写要慢几个数量级,那么为什么偏爱硬盘呢?首先需要说明的是,磁盘运行缓慢主要是因为磁盘的读写比较耗时。读写耗时主要有三个部分:寻道时间+旋转时间+传输时间,其中寻道时间最长。因为寻道需要将磁头移动到相应的磁道上,而电机带动磁臂移动,属于机械运动,因此耗时较长。同时,我们对磁盘的操作通常是随机读写,需要频繁的将磁头移动到对应的磁道上,这就延长了耗时,表现出比较低的性能。看来如果想让磁盘读写速度更快,只要不使用随机读写,或者减少随机次数,就可以有效提高磁盘读写速度。怎么做?顺序读写先说第一种方法,如何用顺序读写代替随机读写?上面说了寻道时间是最长的,所以最直观的想法就是把这部分时间省下来,顺序IO正好可以满足需求。Appendwriting是一个典型的顺序IO,用这个思路优化的典型产品是消息队列。以流行的Kafka为例。Kafka为了实现高性能IO,使用了很多优化方法,其中就用到了顺序写的优化方法。Kafka提供了时间复杂度为O(1)的消息持久化能力,即使是TB级别以上的数据也能保证时间复杂度恒定的访问性能。对于每个partition,它依次将从Producer接收到的消息写入对应的日志文件,当一个文件写满后,再打开一个新的文件。消费的时候也是从全局的某个位置开始,也就是从某个日志文件的某个位置开始,依次读取消息。看完了顺序的方法,我们再来看看减少随机写次数的方法。在很多场景下,为了方便我们后续读取和操作数据,我们要求写入硬盘的数据是有序的。例如在MySQL中,索引在InnoDB引擎中是以B+树的形式组织的,而MySQL主键是聚簇索引(一种索引类型,数据和索引数据放在一起),由于数据和索引数据放在一起,那么在插入或更新数据时,我们需要找到要插入的位置,然后将数据写入特定位置,这会产生随机IO。因此,如果我们每次插入和更新数据时都将数据写到.ibd文件中,那么磁盘就必须找到相应的记录,然后进行更新。整个过程的IO成本和搜索成本非常高,数据库的性能和效率都会大大降低。为了解决写性能问题,InnoDB引入了WAL机制,更准确的说是redolog。下面简单介绍一下RedoLog。InnoDB重做日志是顺序写入的、固定大小的环形日志。主要有两个作用:提高InnoDB存储引擎写入数据的效率和保证crash-safe能力。这里我们只关心它是如何提高写入数据的效率的。下图是重做日志的示意图。从图中可以看出红色olog的写入是顺序写入的,不需要找具体的索引位置,只是简单的从write-pos指针位置追加。其次,当执行写事务或更新事务时,InnoDB首先获取对应的Page,然后进行修改。当事务提交时,位于内存中的redologbuffer被强制刷新到硬盘。如果不考虑binlog,我们可以认为事务执行可以成功返回,写入DB的操作由另一个线程异步执行。然后,缓冲池中的脏页,也就是我们上面修改过的页,可以通过InnoDB的MasterThread定时刷入磁盘,此时修改的数据才真正写入到.ibd文件中。小结很多时候,我们不得不将数据存储在硬盘上,但是因为硬盘相对于内存来说实在是太“慢”了。所以我们必须想办法提高性能。文章总结了两种方法。第一种是appending,使用sequentialIO实现快速写入;二是很多数据库为了提高性能引入了WAL机制。文章以Kafka和MySQL的InnoDB存储引擎为例。除了这两个,有兴趣的同学还可以看看LSM树和etcd是如何实现写快的。推荐阅读【白话科普】10s从头看懂H5。面试官问,Redis是单线程的还是多线程的?我很困惑
