硬盘的存储原理和内部架构

首先,让我们看一下硬盘的发展史:
  • 1956年9月13日,IBM的IBM 350 RAMAC(Random Access Method of Accounting and Control)是现代硬盘的雏形,整个硬盘需要50个直径为24英寸表面涂有磁浆的盘片,它相当于两个冰箱的体积,不过其存储容量只有5MB。
  • 1971年,IBM开始采用一种名叫Merlin的技术生产硬盘,这种技术据称能使硬盘头更好地在盘片上索引。
  • 1973年,IBM 3340问世,主流采用采用红色。这个大家伙每平方英寸存储1.7MB的数据,在当时已经创了一个纪录。许多公司共享这些系统,需要时按照时间和存储空间租用它。租赁价值为7.81美元每兆,这个价格比当时汽油的价格还贵38%。它拥有“温彻斯特”这个绰号,也就是我们现在所熟知的“温氏架构”。来源于它两个30MB的存储单元,恰好是当时出名的“温彻斯特来福枪”的口径和填弹量。至此,硬盘的基本架构被确立。
  • 1979年,IBM发明了Thin Film磁头,使硬盘的数据定位更加准确,因此使得硬盘的密度大幅提升。
  • 1980年,两位前IBM员工创立的公司开发出5.25英寸规格的5MB硬盘,这是首款面向台式机的产品,而该公司正是希捷公司(Seagate)公司。
  • 1982年,日立发布了全球首款容量超过1GB的硬盘。这就是容量为1.2GB的H-8598硬盘。这块硬盘拥有10片14英寸盘片,两个读写磁头。
  • 1980年代末,IBM推出MR(Magneto Resistive磁阻)技术令磁头灵敏度大大提升,使盘片的存储密度较之前的20Mbpsi(bit/每平方英寸)提高了数十倍,该技术为硬盘容量的巨大提升奠定了基础。1991年,IBM应用该技术推出了首款3.5英寸的1GB硬盘。
  • 1970年到1991年,硬盘碟片的存储密度以每年25%~30%的速度增长;从1991年开始增长到60%~80%;至今,速度提升到100%甚至是200%。从1997年开始的惊人速度提升得益于IBM的GMR(Giant Magneto Resistive,巨磁阻)技术,它使磁头灵敏度进一步提升,进而提高了存储密度。
  • 1993年,康诺(Conner Peripherals)推出了CP30344硬盘容量是340MB。
  • 1995年,为了配合Intel的LX芯片组,昆腾与Intel携手发布UDMA 33接口—EIDE标准将原来接口数据传输率从16.6MB/s提升到了33MB/s。同年,希捷开发出液态轴承(FDB,Fluid Dynamic Bearing)马达。所谓的FDB就是指将陀螺仪上的技术引进到硬盘生产中,用厚度相当于头发直径十分之一的油膜取代金属轴承,减轻了硬盘噪音与发热量。
  • 1996年,希捷收购康诺(Conner Peripherals)
  • 1998年2月,UDMA 66规格面世。
  • 2000年10月,迈拓(Maxtor)收购昆腾。
  • 2003年1月,日立宣布完成20.5亿美元的收购IBM硬盘事业部计划,并成立日立环球存储科技公司(Hitachi Global StorageTechnologies, Hitachi GST)。
  • 2005年日立环储和希捷都宣布了将开始大量采用磁盘垂直写入技术(perpendicular recording),该原理是将平行于盘片的磁场方向改变为垂直(90度),更充分地利用的存储空间。
  • 2005年12月21日,希捷宣布收购迈拓(Maxtor)。
  • 2007年1月,日立环球存储科技宣布将会发售全球首只1Terabyte的硬盘,比原先的预定时间迟了一年多。硬盘的售价为399美元,平均每美分可以购得27.5MB硬盘空间。
  • 2011年3月,西部数据以43亿美元的价格,收购日立环球存储科技。
  • 2011年4月,希捷宣布与三星强化策略伙伴关系。

    从硬盘问世至今已经过了56个年头,不管是容量、体积还是生产工艺都较之前有了重大革新和改进,但一直都保持了“温氏”的架构(固态硬盘除外,它不是我们今天的主角)。经过封装后的硬盘,对我们一般呈现出如下的样子:
    背面:
    打开后盖:

    硬盘主要由盘体、控制电路板和接口部件组成。盘体就是一个密封,封装了多个盘片的腔体;控制电路包含硬盘BIOS,主控芯片和硬盘缓存等单元;接口部件包含电源、数据接口主从跳线等。

    硬盘的盘片一般采用合金材料,多数为铝合金(IBM曾经开发过玻璃材质的盘片,好像现在有些厂家也生产玻璃材质的盘片,但不多见),盘面上涂着磁性材料,厚度一般在0.5mm左右。有些硬盘只装一张盘片,有些则有多张。硬盘盘片安装在主轴电机的转轴上,在主轴电机的带动下作高速旋转。每张盘片的容量称为单碟容量,而一块硬盘的总容量就是所有盘片容量的总和。早期硬盘由于单碟容量低,所以盘片较多。现代的硬盘盘片一般只有少数几片。 盘片上的记录密度很大,而且盘片工作时会高速旋转,为保证其工作的稳定,数据保存的长久,所以硬片都是密封在硬盘内部。不可自行拆卸硬盘,在普通环境下空气中的灰尘、指纹、头发丝等细小杂质都会对硬盘造成永久损害。一个被大卸八块的硬盘如下:

    接下来我们了解一下硬盘的盘面,柱面,磁道和扇区的概念。

 

    盘面

    硬盘一般会有一个或多个盘片,每个盘片可以有两个面(Side),即第1个盘片的正面称为0面,反面称为1面;第2个盘片的正面称为2面,反面称为3面...依次类推。每个盘面对应一个磁头(head)用于读写数据。第一个盘面的正面的磁头称为0磁头,背面称为1磁头;第二个盘片正面的磁头称为2磁头,背面称为3磁头,以此类推。盘面数和磁头数是相等的。


    一张单面的盘片需要一个磁头,双面的盘片则需要两个磁头。硬盘采用高精度、轻型磁头驱动和定位系统。这种系统能使磁头在盘面上快速移动,读写硬盘时,磁头依靠磁盘的高速旋转引起的空气动力效应悬浮在盘面上,与盘面的距离不到1微米(约为头发直径的百分之一),可以在极短的时间内精确定位到计算机指令指定的磁道上。

    早期由于定位系统限制,磁头传动臂只能在盘片的内外磁道之间移动。因此,不管开机还是关机,磁头总在盘片上。所不同的是,关机时磁头停留在盘片启停区,开机时磁头“飞行”在磁盘片上方。

 

    磁道

    每个盘片的每个盘面被划分成多个狭窄的同心圆环,数据就是存储在这样的同心圆环上,我们将这样的圆环称为磁道(Track),每个盘面可以划分多个磁道。关机时磁头停留在硬盘的着陆区(Landing Zone),这个着陆区以前是位于离盘心最近的区域,不存放任何数据。在后期的硬盘工艺中有些硬盘生产厂商将这个区域被移动到了盘片的外面,如下所示:

    在每个盘面的最外圈,离盘心最远的地方是“0”磁道,向盘心方向依次增长为1磁道,2磁道,等等。硬盘数据的存放就是从最外圈开始。

 

    扇区

    根据硬盘规格的不同,磁道数可以从几百到成千上万不等。每个磁道上可以存储数KB的数据,但计算机并不需要一次读写这么多数据。在这一这基础上,又把每个磁道划分成若干弧段,每段称为一个扇区(Sector)。扇区是硬盘上存储的物理单位,每个扇区可存储128×2N次方(N=0,1,2,3)字节的数据。从DOS时代起,每扇区是128×22=512字节,现在已经成了业界不成文的规定,也没有哪个硬盘厂商试图去改变这种约定。也就是说即使计算机只需要硬盘上存储的某个字节,也须一次把这个字节所在的扇区中的全部512字节读入内存,再选择所需的那个字节。扇区的编号是从1开始,而不是0,这一点需要注意。另外,硬盘在划分扇区时,和软盘是有一定区别的。软盘的一个磁道中,扇区号一般依次编排,如1号,2号,3号...以此类推。但在硬盘磁道中,扇区号是按照某个间隔跳跃着编排。比如,2号扇区并不是1号扇区后的按顺序的第一个而是第八个,3号扇区又是2号扇区后的按顺序的第八个,依此类推,这个“八”称为交叉因子。

    这个交叉因子的来历有必要详述一下,我们知道,数据读取经常需要按顺序读取一系列相邻的扇区(逻辑数据相邻)。如对磁道扇区按物理顺序进行编号,很有可能出现当磁头读取完第一个扇区后,由于盘片转速过快来不及读取下一个扇区,(要知道物理相邻扇区位置距离是极小的),必须等待转完一圈,这极大浪费了时间。所以就用交叉来解决这个问题。增加了交叉因子后的扇区编号一般是下面这个样子:

    柱面

    柱面其实是我们抽象出来的一个逻辑概念,前面说过,离盘心最远的磁道为0磁道,依此往里为1磁道,2磁道,3磁道....,不同面上相同磁道编号则组成了一个圆柱面,即所称的柱面(Cylinder)。这里要注意,硬盘数据的读写是按柱面进行,即磁头读写数据时首先在同一柱面内从0磁头开始进行操作,依次向下在同一柱面的不同盘面(即磁头上)进行操作,只有在同一柱面所有的磁头全部读写完毕后磁头才转移到下一柱面,因为选取磁头只需通过电子切换即可,而选取柱面则必须通过机械切换。电子切换比从在机械上磁头向邻近磁道移动快得多。因此,数据的读写按柱面进行,而不按盘面进行。 读写数据都是按照这种方式进行,尽可能提高了硬盘读写效率。

 

    

    将物理相邻的若干个扇区称为了一个簇。操作系统读写磁盘的基本单位是扇区,而文件系统的基本单位是簇(Cluster)。在Windows下,随便找个几字节的文件,在其上面点击鼠标右键选择属性,看看实际大小与占用空间两项内容,如大小:15 字节 (15 字节), 占用空间:4.00 KB (4,096 字节)。这里的占用空间就是你机器分区的簇大小,因为再小的文件都会占用空间,逻辑基本单位是4K,所以都会占用4K。 簇一般有这几类大小 4K,8K,16K,32K,64K等。簇越大存储性能越好,但空间浪费严重。簇越小性能相对越低,但空间利用率高。NTFS格式的文件系统簇的大小为4K。


    硬盘读写数据的过程

    现代硬盘寻道都是采用CHS(Cylinder Head Sector)的方式,硬盘读取数据时,读写磁头沿径向移动,移到要读取的扇区所在磁道的上方,这段时间称为寻道时间(seek time)。因读写磁头的起始位置与目标位置之间的距离不同,寻道时间也不同。目前硬盘一般为2到30毫秒,平均约为9毫秒。磁头到达指定磁道后,然后通过盘片的旋转,使得要读取的扇区转到读写磁头的下方,这段时间称为旋转延迟时间(rotational latencytime)。

 

    一个7200(转/每分钟)的硬盘,每旋转一周所需时间为60×1000÷7200=8.33毫秒,则平均旋转延迟时间为8.33÷2=4.17毫秒(平均情况下,需要旋转半圈)。平均寻道时间和平均选装延迟称为平均存取时间。

 

    所以,最后看一下硬盘的容量计算公式:

    硬盘容量=盘面数×柱面数×扇区数×512字节

 

 

    在博文“Linux启动过程分析”中我们提到过MBR,它是存在于硬盘的0柱面,0磁头,1扇区里,占512字节的空间。这512字节里包含了主引导程序Bootloader和磁盘分区表DPT。其中Bootloader占446字节,分区表占64字节,一个分区要占用16字节,64字节的分区表只能被划分4个分区,这也就是目前我们的硬盘最多只能支持4个分区记录的原因。

    即,如果你将硬盘分成4个主分区的话,必须确保所有的磁盘空间都被使用了(这不是废话么),一般情况下我们都是划分一个主分区加一个扩展分区,然后在扩展分区里再继续划分逻辑分区。当然,逻辑分区表也需要分区表,它是存在于扩展分区的第一个扇区里,所以逻辑分区的个数最多也只能有512/16=32个,并不是想分多少个逻辑分区都可以。

    注意,我们所说的扩展分区也是要占用分区表项的。例如,如果我们的硬盘只划分一个主分区和一个逻辑分区,此时的分区表的排列如下:

   Device Boot      Start         End      Blocks   Id  System

/dev/sda1   *           1          19      152586   83  Linux

/dev/sda2              20        2569    20482875   83  Extended

/dev/sda5            2570        19457     4128705   82  Linux

    主分区为1号分区,扩展分区占用了2号分区,3和4号扩展分区被预留了下来,逻辑分区从5开始编号依次递增,这里我们只划分了一个逻辑分区。


磁盘读写数据所花费的时间

在了解了硬盘的基本原理之后,不难推算出,磁盘上数据读取和写入所花费的时间可以分为三个部分。

1.寻道时间

所谓寻道时间,其实就是磁臂移动到指定磁道所需要的时间,这部分时间又可以分为两部分:
寻道时间=启动磁臂的时间+常数*所需移动的磁道数
其中常数和驱动器的的硬件相关,启动磁臂的时间也和驱动器的硬件相关

2.旋转延迟

旋转延迟指的是把扇区移动到磁头下面的时间。这个时间和驱动器的转数有关,我们通常所说的7200转的硬盘的转就是这个。
平均旋转延迟=1/(2*转数每秒)
比如7200转的硬盘的平均旋转延迟等于1/2*120≈4.17ms
旋转延迟只和硬件有关。

3.传输时间
传输时间指的是从磁盘读出或将数据写入磁盘的时间。
这个时间等于:所需要读写的字节数/每秒转速*每扇区的字节数


磁盘调度算法

通过上面硬盘读写数据所分的三部分时间不难看出,大部分参数是和硬件相关的,操作系统无力优化。只有所需移动的磁道数是可以通过操作系统来进行控制的,所以减少所需移动的磁道数是减少整个硬盘的读写时间的唯一办法。

因为操作系统内可能会有很多进程需要调用磁盘进行读写,因此合理的安排磁头的移动以减少寻道时间就是磁盘调度算法的目的所在,几种常见的磁盘调度算法如下。

1.先来先服务算法(FCFS)

这种算法将对磁盘的IO请求进行排队,按照先后顺序依次调度磁头。这种算法的特点是简单,合理,但没有减少寻道时间

2.最短寻道时间算法(SSFT)

这种算法优先执行所需读写的磁道离当前磁头最近的请求。这保证了平均寻道时间的最短,但缺点显而易见:离当前磁头比较远的寻道请求有可能一直得不到执行,这也就是所谓的“饥饿现象”。

3.扫描算法(SCAN)

这种算法在磁头的移动方向上选择离当前磁头所在磁道最近的请求作为下一次服务对象,这种改进有效避免了饥饿现象,并且减少了寻道时间。但缺点依然存在,那就是不利于最远一端的磁道访问请求。

3.循环扫描算法(CSCAN)

也就是俗称的电梯算法,这种算法是对最短寻道时间算法的改进。这种算法就像电梯一样,只能从1楼上到15楼,然后再从15楼下到1楼。这种算法的磁头调度也是如此,磁头只能从最里磁道到磁盘最外层磁道。然后再由最外层磁道移动到最里层磁道,磁头是单向移动的,在此基础上,才执行和最短寻道时间算法一样的,离当前磁头最近的寻道请求。这种算法改善了SCAN算法,消除了对两端磁道请求的不公平。

其它优化手段以及SQL Server是如何利用这些手段

除去上面通过磁盘调度算法来减少寻道时间之外。还有一些其它的手段同样可以利用,在开始之前,我首先想讲一下局部性原理。

局部性原理

所谓的局部性原理分为时间和空间上的。由于程序是顺序执行的,因此当前数据段附近的数据有可能在接下来的时间被访问到。这就是所谓的空间局部性。而程序中还存在着循环,因此当前被访问的数据有可能在短时间内被再次访问,这就是所谓的时间局部性原理。

因此在了解了局部性原理之后,我们可以通过以下几个手段来减少磁盘的IO。

提前读(Read-Ahead)

提前读也被称为预读。根据磁盘原理我们不难看出,在磁盘读取数据的过程中,真正读取数据的时间只占了很小一部分,而大部分时间花在了旋转延迟和寻道时间上,因此根据空间局部性原理,SQL Server每次读取数据的时间不仅仅读取所需要的数据,还将所请求数据附近的数据进行读取。这在SQL Server中被称为预读。SQL Server通过预读可以有效的减少IO请求。

延迟写(Delayed write)

同样,根据时间局部性原理,最近被访问的数据有可能再次被访问,因此当数据更改之后不马上写回磁盘,而是继续放在内存中,以备接下来的请求读取或者修改,是减少磁盘IO的另一个有效手段,在SQL Server中,实现延迟写是buffer pool,当一个修改请求被commit之后,并不会立刻写回磁盘,而是将修改的页标记为“脏”,然后根据某种机制通过checkpoint或lazy writer写回磁盘,关于checkpoint和lazy writer的原理,可以参考我之前的文章:浅谈SQL Server中的事务日志(二)----事务日志在修改数据时的角色.

优化物理分布

根据磁盘原理不难看出,如果所请求的数据在磁盘物理磁道之间是连续的,那么会减少磁头的移动距离,从而减少了寻道时间。因此相关的数据放在连续的物理空间上会减少寻道时间。SQL Server中,通过聚集索引使得数据根据主键在物理磁盘上连续,从而减少了寻道时间。

说明:本文转自blog.csdn.net,用于学习交流分享,仅代表原文作者观点。如有疑问,请联系我们删除~