简介
在项目中往往应用内存来做缓存,这样做是为了避免磁盘I/O,特别是随机磁盘I/O,这里梳理了磁盘的基础知识,和mysql 如何避免磁盘随机I/O
磁盘的物理结构
硬盘内部主要部件为磁盘盘片、传动手臂、读写磁头和主轴马达。实际数据都是写在盘片上,读写主要是通过传动手臂上的读写磁头来完成。实际运行时,主轴让磁盘盘片转动,然后传动手臂可伸展让读取头在盘片上进行读写操作。磁盘物理结构如下图所示:、
由于单一盘片容量有限,一般硬盘都有两张以上的盘片,每个盘片有两面,都可记录信息,所以一张盘片对应着两个磁头。盘片被分为许多扇形的区域,每个区域叫一个扇区,硬盘中每个扇区的大小固定为512字节。盘片表面上以盘片中心为圆心,不同半径的同心圆称为磁道,不同盘片相同半径的磁道所组成的圆柱称为柱面。磁道与柱面都是表示不同半径的圆,在许多场合,磁道和柱面可以互换使用。磁盘盘片垂直视角如下图所示:
影响硬盘性能的因素
寻道时间
Tseek是指将读写磁头移动至正确的磁道上所需要的时间。寻道时间越短,I/O操作越快,目前磁盘的平均寻道时间一般在3-15ms。
旋转延迟
Trotation是指盘片旋转将请求数据所在的扇区移动到读写磁盘下方所需要的时间。旋转延迟取决于磁盘转速,通常用磁盘旋转一周所需时间的1/2表示。比如:7200rpm的磁盘平均旋转延迟大约为60*1000/7200/2 = 4.17ms,而转速为15000rpm的磁盘其平均旋转延迟为2ms。
数据传输时间
Ttransfer是指完成传输所请求的数据所需要的时间,它取决于数据传输率,其值等于数据大小除以数据传输率。目前IDE/ATA能达到133MB/s,SATA II可达到300MB/s的接口数据传输率,数据传输时间通常远小于前两部分消耗时间。简单计算时可忽略。
衡量性能的指标
机械硬盘的连续读写性能很好,但随机读写性能很差,这主要是因为磁头移动到正确的磁道上需要时间,随机读写时,磁头需要不停的移动,时间都浪费在了磁头寻址上,所以性能不高。衡量磁盘的重要主要指标是IOPS和吞吐量。
IOPS
IOPS(Input/Output Per Second)即每秒的输入输出量(或读写次数),即指每秒内系统能处理的I/O请求数量。随机读写频繁的应用,如小文件存储等,关注随机读写性能,IOPS是关键衡量指标。可以推算出磁盘的IOPS = 1000ms / (Tseek + Trotation + Transfer),如果忽略数据传输时间,理论上可以计算出随机读写最大的IOPS。常见磁盘的随机读写最大IOPS为:
7200rpm的磁盘 IOPS = 76 IOPS
10000rpm的磁盘IOPS = 111 IOPS
15000rpm的磁盘IOPS = 166 IOPS
吞吐量
吞吐量(Throughput),指单位时间内可以成功传输的数据数量。顺序读写频繁的应用,如视频点播,关注连续读写性能、数据吞吐量是关键衡量指标。它主要取决于磁盘阵列的架构,通道的大小以及磁盘的个数。不同的磁盘阵列存在不同的架构,但他们都有自己的内部带宽,一般情况下,内部带宽都设计足够充足,不会存在瓶颈。磁盘阵列与服务器之间的数据通道对吞吐量影响很大,比如一个2Gbps的光纤通道,其所能支撑的最大流量仅为250MB/s。最后,当前面的瓶颈都不再存在时,硬盘越多的情况下吞吐量越大。
平和内存和磁盘资源
配置大量内存最大的原因其实不是因为可以在内存中保存大量数据:最终目的是避免磁盘I/O,因为磁盘I/O比在内存中访问数据要慢的多,计算机包含一个金字塔型的缓存体系,更小,更快更昂贵的缓存在顶端,如图
在这个高速缓存层次中,最好是利用各级缓存来存放热点数据,以获取更快的访问速度,通常使用一些启发式的方法,如最近被使用的数据很可能会很快的再次使用,以及相邻的数据可能很快需要使用。设计良好的数据库缓存(如innoDB缓冲池),其效率通常超过操作系统的缓存,因为操作系统的缓存是为通用任务设计的,数据库缓存更了解数据库存储的需求,它包含特殊的用途和逻辑(例如写入顺序)一帮助满足这些需求。
随机I/O和顺序I/O
数据库服务器同事使用顺序I/O和随机I/O,随机I/O从缓存中受益最多,因为缓存这些数据将有助于避免昂贵的磁盘寻道,相反顺序读取一般只需要扫描一次数据,所以缓存对它是没用的,除非能完全放在内存中缓存起来。
顺序读取不能从缓中受益另一个原因是它们比随机读取快,有以下两个原因:
顺序I/O比随机I/O快
顺序操作的执行速度比随机操作快,无论是在内存还是在磁盘上。假设磁盘每秒可以做100个随机I/O操作,并且可以完成每秒50M的顺序读取(在大概是消费级磁盘现在能达到的水平)。如果每行100字节,随机读取可以每秒读取100行,相比之下顺序读取可以读取每秒500000行这个是随机读取的5000倍,或几个数量级的差异,因此这种情况下随机I/O可以从缓冲中获得很多好处。(注:因为随机读取受到IOPS的限制—100个随机I/O,而顺序读取受到吞吐量的限制—这里是每秒50M)
顺序访问内存行的数据也快于随机访问。现在的内存芯片通常每秒可以随机访问约250000次100字节的行,或者每秒500万次的顺序访问。请注意,内存随机访问速度比磁盘随机访问快了2500倍,而内存中顺序访问通常只有磁盘的10倍的数据。
存储引擎执行顺序读取比随机快
一个随机读取一般意味着存储引擎必须执行索引操作,通常需要通过B树的数据结构查找,并且和其它值比较(innoDB中如果不是覆盖索引则需要回表查询主键索引的B树查询全部数据)。相反,连续读取一般需要遍历一个简单的数据结构,如链表。这样就少了很多工作,反复这样操作,连续读取的速度就比随机读取要快了。
最后随机读取通常只要查找特定的行,但不仅仅只读取一样–而是要读取一整页的数据(因为页数数据再磁盘中存储的最小单位,innodbDB中也可以配置相应的最小页的大小),其中大部分都是不需要的,这浪费了很多工作。另一方方面,顺序读取数据,通常发生在想要的页面上的所有行,所以更符合成本效益。
缓存,读和写
如果有足够的内存,就完全可以避免磁盘读取请求,如果所有数据文件都可以放在内存中,一旦服务器缓存热起来,所有读取都可以在缓存中命中,虽然有逻辑读取,但是物理读取就没有了。但是写入是不同的问题,写入可以像读一样在内存中完成,但是迟早要写入到磁盘中,所以它需要持久化,换一句话说,缓存可以延迟写入,但是不能像消除读取一样消除写入。
主要通过以下两个方法解决
多次写入,一次刷新
一片数据可以在内存中改变很多次,而不需要吧所有新值写到磁盘,当数据最终被刷新到磁盘后,最后一次物理写入之前发生的所有修改都被持久化了,例如许多语句可以更新内存中的计数器,如果计数器递增100次,然后写入磁盘,100次修改就被合并成一次写入。
I/O合并
许多不同部分的数据可以在内存中修改,并且这些修改可以合并在一起,通过一次磁盘操作完成物理写入。
这就是为什么许多交易系统使用预写日志(WAL)策略。预写日志采用在内存中变更页面,而不是马上刷新到磁盘上的策略,因为刷新磁盘通常需要随机I/O,这非常慢。相反,如果吧变化记录写到一个连续的日志文件中,这就很快了。后台线程可以稍后把修改的页面刷新到磁盘,并且在刷新过程中优化写操作,写入操作从缓冲中受益很大,因为它把随机I/O更多的转换到连续I/O.(注:innodb的事务日志就是这样做的,innodb用日志把随机I/O变为顺序I/O,每次事务的变化将其顺序的写入到日志文件中,后续再由后台进程将数据写入到数据文件中)。