深刻理解数据库磁盘存储(Disk Storage)

数据库管理系统将数据存储在磁盘、磁带以及其余的裸设备上,虽然这些设备的访问速度相比内存慢不少,但其非易失性和大容量的特色使他们成为数据存储的不二之选。html

本文主要讨论大型数据库产品的磁盘存储内部结构,这对于深刻理解数据库各类数据结构具备相当重要的做用。数据库

数据库磁盘存储的体系结构服务器

以上两图分别展现了存储器分级结构以及磁盘内部物理结构,不是本文重点,不赘述。须要强调的是:一次完整的输入输出(IO)操做的时间=磁盘轴旋转时间(旋转延迟)+磁盘臂移动时间(寻道时间)+数据传输时间。三者所需时间的平均经验值为:0.004秒、0.008秒和0.0005秒。因此,一次完整的IO时间的经验值是0.0125秒,即1/80秒。数据结构

对于大型数据库而言,即使是这极短暂的0.0125秒,频繁的IO操做会将这微不足道的时间累积得很是可观,所以磁盘存储的优化对于数据库效率的提高是很是必要和重要的。不一样的数据库产品的磁盘存储内部实现是不一样的,本文只讨论Oracle、DB2大型数据库产品,两者细节上虽有不一样,但结构大致上是同样的。并发

Oracle和DB2数据库的存储模型如图:oracle

能够看出,数据库中的数据都存储在表空间中。表空间便是管理将逻辑数据库设计映射到操做系统物理存储中的一个数据库对象,用于指明数据的物理位置。关于表空间,之后再讨论。jsp

Oracle数据库磁盘存储的逻辑结构为:一个数据库(Database)对应多个表空间(Tablespace),一个表空间对应多个段(Segment),一个段对应多个区(Extent),一个区对应多个数据块(Data Block),真正的数据就保存在数据块中。这里有如下几点须要说明:数据库设计

1.Oracle中一个数据块的大小默认是2KB(支持2KB,4KB,8KB,16KB,32KB),而DB2中则默认是4KB(支持4KB,8KB,16KB,32KB);性能

2.Oracle中有段(Segment)的概念,而DB2中没有这一律念,表空间直接是各个容器(数据文件)中的区(Extent)组成,不过也仍是有一个很弱化的Extent组概念。下面提到的关于Segment的内容则所有是针对Oracle的;优化

3.Oracle中的数据块称为Oracle Block,而DB2中则直接称为Data Page(数据页)。

4.Oracle中的Extent称为区,而DB2中则称为扩展数据块。为方便阅读,本文中统称为区。

                                 Oracle磁盘存储逻辑结构

 

  

                                           DB2磁盘存储逻辑结构

要注意:这里的表空间,段,区,数据块(页)所有都是数据库中的逻辑概念,并非物理存在的,那么数据库磁盘存储的逻辑结构如何映射到操做系统磁盘存储的物理结构中呢?

先来看看表空间的物理映射。表空间在操做系统上是由容器(Container)承载的,对于系统管理表空间(SMS)【注意:Oracle不存在系统管理表空间,其表空间所有都是数据库管理的】,其惟一的容器是文件目录;而对于数据库管理表空间,其容器则是文件或者裸设备(好比磁带、磁盘柜)。这里咱们不讨论系统管理表空间,只关注数据库管理表空间下的数据库磁盘存储。因为数据库对于文件和裸设备这两种容器操做上是同等对待的,因此以文件容器为例进行讨论。文件容器自己是一个由DMS表空间使用的预分配大小的文件,一个表空间能够有多个文件容器,即有多个数据文件。也就是说:一个逻辑上的表空间映射为多个物理上的数据文件。

再来讨论数据块(页)的物理映射。咱们知道,物理结构上,操做系统中的文件是由多个操做系统的块(Block)组成的(Linux,Unix系统的块大小为512B,而Windows系统的块大小为1KB),而上面说了,数据库中的数据是以数据块(页)为单位存放的,,那么数据库中的数据块(页)和操做系统的块是什么关系呢?事实上,若干个操做系统块组成一个数据库的数据块(页)。对应区(extent)和段(segment)的映射并无物理单位与之对应,而是在一个数据文件中会包含多个段,每一个段里又包含多个区(每一个段中的区的个数不必定是同样的;DB2中数据文件中直接以区为单位,没有段的概念),每一个区包含若干数据块(每一个区中数据块的数量也不必定同样)。另外,和表空间同样,段也是能够跨文件的,即一个段能够由不一样文件中的区所组成。

数据库磁盘存储的逻辑/物理映射图解以下(DB2中没有Segment这一层):

关于这些逻辑单位的具体讨论之后再说。如今只须要了解大致的体系结构。经过上面的介绍能够看到,数据库管理的表空间自成一体,具备平台无关性,俨然是一个小型的独立文件系统了。

数据库磁盘存储的内部实现

了解了磁盘存储的体系结构,再来看一看数据库系统中,数据具体是如何进行存储的。

当建立一张表(Table)的时候,会为其指定表空间,一旦表成功建立,数据库系统就要为表提供磁盘空间。

Oracle数据库会自动为一张表分配一个Segment(段),这个Segment称为Data Segment(数据段)【注:一张表只能被分配一个数据段。Oracle一共有四种类型的段,分别是Data Segment(数据段),Index Segment(索引段),Rollback Segment(回滚段)和Temp Segment(临时段)】。当为表分配的数据段所有写满的时候,数据库管理系统会为这个数据段增长新的区(Extent),也就是说,数据段空间分配完后并非须要多少空间就为段增长多少空间,也不是直接在区中增长数据块,而是一次性增长一个Extent(这样作避免了频繁的Segment扩容),Extent是空间分配的最小单位,并且Extent在表空间中的各个容器上是均衡分配的。另外,数据块(页)是最小的存储单位,也即最小的I/O单位,全部数据都是按块(页)存储,读出的时候也是直接将整个数据块(页)读入内存中的。至于DB2,其方案与Oracle基本相同,所不一样的是没有Segment分配的问题。

咱们以DB2为例,看看数据存储具体是怎样的。【参考牛新庄:深刻解析DB2】

一个DMS表空间能够有多个容器(文件或裸设备),DB2将Extent均衡的写到各个容器上。即当须要请求一个Extent时,数据库管理器这个Extent分配到下一个容器上。这种方案保证了各容器的均衡利用,提升了并行访问效率。如左图:

                                         表空间容器均衡写

 

 

                      表空间容器,Extent,数据页和表空间之间的关系

 

新建一个称为HUMANRES的DMS表空间,其Extent大小为2个Page(即数据块),每一个Page大小为4KB。表空间有4个容器,每一个容器内有少许已分配的Extent:表空间中DEPARTMENT和EMPLOYEE表中都占用了7个Page,按照上面介绍的分配规则,这两个表中的数据都会写到四个不一样的容器中(7个Page须要分配4个Extent,每一个Extent依次分配到各个容器中),另外,一个Extent只能由一个表所写,即使表数据不能彻底利用Extent中的空间,Extent中的空闲空间也只能空着,不能继续写入其余表中的数据(这是由Extent是空间最小分配单位决定的)。如右图。

数据块(页)存储

下面进一步深刻,分析一下数据库磁盘存储最小单元数据块(Data Block or Data Page)内的具体结构是怎样的。对于Oracle和DB2,两者的数据块结构是不一样的。如下分别讨论。至于其余的数据库产品,不在讨论之列。

Oracle数据库的数据块(Data Block)结构及相关特性

(参照:http://docs.oracle.com/cd/B28359_01/server.111/b28318/logical.htm

数据块格式

Oracle 数据块的格式不管是表、索引仍是cluster 数据,格式都是很相似的,如图:

 

公共和变量块头( Common and Variable Header)

头部包含了通用块信息,例如块的地址和段的类型 ( 例如表或者索引 )
表目录(Table Directory)
这部分信息包含了在这个块中该表或该索引的相关信息。
行目录(Row Directory)
这部分包含了数据块中的实际行的信息 ( 包括行数据区域中每行的地址 ) ,一旦数据块头部的这个行目录的空间被分配了,那么即便该行删除了,这段空间仍然不能回收。
所以一个当前为空的数据块,此区域包含数据块中存储的数据行的信息(每一个数据行片段( row piece ) 在行数据区( row data area )中的地址)。 [ 一个数据块中可能保存一个完整的数据行,也可能只保存数据行的一部分 ,因此文中使用 row piece] 。只有在数据块中插入( insert )新数据时,行目录区空间才会被从新利用。
头部信息区(Overhead )
块头( header/Common and Variable ),表目录( Table Directory ),行目录( Row Directory )这三部分合称为头部信息区( Overhead )。头部信息区不存放数据,它存放的整个块的信息。头部信息区的大小是可变的。通常来讲,头部信息区的大小介于 84 字节( bytes )到 107 字节( bytes )之间。
可用空间(Free Space)
可用空间是一个块中未使用的区域,这片区域用于新行的插入和已经存在的行的更新。可用空间也包含事务条目,当每一次 insert 、 update 、 delete 、 select ..for update 语句访问块中一行或多行数据,将会请求一条事务条目,事务条目的请求空间与操做系统相关,在多数操做系统中大约所需 23 个字节。
行数据(Row Data)
这部数据块包含了表或索引的数据,行也可能跨数据块,这也就是行迁移现象。
 
可用空间管理(Free Space Management)
可用空间多是自动或人工管理。
可用空间是由 Oracle 内部的段自动管理的,段内的可用 / 已用空间用位图来跟踪。自动段空间管理提供了如下好处:
l  使用便捷
l  更好的空间利用率,特别是那些行大小变化很大的对象
l  并发访问的动态调整
l  性能 / 空间的平衡
数据块可用空间的利用和压缩
Delete 和 update( 把原值变小 ) 可增长数据块的可用空间。在如下状况下 insert 语句才能有效地利用已释放的空间。
假如 insert 语句在同一个事务中,而 insert 前面的语句恰好释放了相应的空间,这时候 insert 语句能够利用该空间
假如 insert 语句与释放空间的语句不在同一个事务中,那么只有当其余事务提交后而且恰好须要空间的时候, insert 语句才能利用该空间。
释放的空间也可能不是连续的,只有当 如下两个条件都知足的时候, Oracle 才会合并和压缩数据块的可用空间。
1 一个 insert 或 update 语句试图使用足够空间建立新行的时候;
2 自由空间是分散的以致于不能插入毗邻空间的时候;
这就是所谓的“块重组(Block Reorganization)”。事实上,Oracle和DB2在对待碎片空间上的策略是一致的,即:行数据被删除后,该空间空置,当块空间不足时进行重组。而其余数据库的策略是行数据删除后,其余行数据顺移以保证可用空间是连续的。显然,这种作法影响数据库效率。
 
行连接和行迁移(Row Chaining and Migrating )
行连接( Row Chaining ):若是咱们往数据库中插入( INSERT )一行数据,这行数据很大,以致于一个数据块存不下一整行, Oracle 就会把一行数据分做几段存在几个数据块中,这个过程叫行连接( Row Chaining )。
若是一行数据是普通行,这行数据可以存放在一个数据块中;若是一行数据是连接行,这行数据存放在多个数据块中。
行连接又称为行跨页,Oracle容许行跨页,但DB2是不容许的。

行迁移 (Row Migrating) :数据块中存在一条记录,用户执行 UPDATE 更新这条记录,这个 UPDATE 操做使这条记录变长,这时候, Oracle 在这个数据块中进行查找,可是找不到可以容纳下这条记录的空间,无奈之下, Oracle 只能把整行数据移到一个新的数据块。原来的数据块中保留一个“指针”,这个“ 指针”指向新的数据块。被移动的这条记录的 ROWID 保持不变。
 
 
PCTFREE和PCTUSED
PCTFREE 参数用于指定块中必须保留的最小空闲空间百分例。之因此要预留这样的空间,是由于 UPDATE 时,须要这些空间。若是 UPDATE 时,没有空余空间, Oracle 就会分配一个新的块,这会产生行迁移( Row Migrating ),进行空间预留可以必定程度上保证数据库访问效率。
PCTUSED 也是用于设置一个百分比,当块中已使用的空间的比例小于这个百分比的时候,这个块才被标识为有效状态。只有有效的块才被容许插入数据。
PCTFREE和PCTUSED做用示意图以下:

DB2数据库的数据页(Data Page)结构及相关特性

 

 

(参照:http://pic.dhe.ibm.com/infocenter/db2luw/v9r7/index.jsp?topic=%2Fcom.ibm.db2.luw.admin.perf.doc%2Fdoc%2Fc0007337.html

数据页格式

 

在标准表中,数据在逻辑上按数据页的列表进行组织。这些数据页根据表空间的Extent大小在逻辑上分组到一块儿。这个Extent组相似于Oracle中的Segment,但Extent组没有Segment那样的强制概念。

每一个数据页都具备相同的格式。每一页的最前面都是页头,后面跟着槽(Slot)目录,而后是可用空间和记录。

页头(Header)

用于存放BPS HEADER和一些页的信息字段,其中BPS HEADER占48字节,整个页头大概占91个字节。

槽目录(Slot Directory)

槽目录相似Oracle的数据块中的行目录,槽目录中的每一个条目都与该页中的一个记录相对应。槽目录中的条目表明记录开始位置在数据页中的字节偏移。值为 -1 的条目与已删除的记录相对应。

可用空间(Free Space)

DB2可用空间严格来讲包括一般意义上的常规可用空间和嵌入的可用空间。其中常规可用空间可用直接存储数据,而嵌入的可用空间一般是记录被删除后产生的碎片空间,不能直接使用,只有当页重组后合并到常规可用空间后才能被使用,这一点和Oracle相似。

记录(Record)

已经存储了数据的空间,相似于Oracle中的行数据。即表中的行存放于页面中成为记录。根据数据页大小以及记录大小的不一样,每一个数据页中包含的记录数(即行数据数)也会有所变化。大多数页仅包含用户记录(即普通的数据库数据)。可是,少数页包含由数据服务器用于管理表的特殊内部记录。例如,在标准表中,每 500 个数据页就有一个可用空间控制记录(FSCR)。这些记录用于映射后续每 500 个数据页(直到下一个 FSCR 为止)中可供新记录使用的可用空间。另外,在DB2 V9以前,每一个数据页最多能够存放255条记录,而DB2 V9以后,每一个数据页理论上能够存放65000条记录(与RID有关,暂不讨论),但实际上受限于数据页大小,每一个数据页大概能存放2300多条记录。

行连接和行迁移

DB2是不容许行(记录)跨页的,即不容许行连接。可是容许行迁移(DB2中又称为行溢出),

当表中存在变长数据类型时,容易发生行溢出现象,行溢出有时也叫行迁移,它表示当咱们更新变长数据类型的字段时,因为更新的数据长度比原数据长,以致于当前的数据页没法存放整行数据时,就须要把这行数据存放到一个新的数据页,并在原来的数据页内存放该行新位置的指针以指向新行位置,被移动的数据的RID(RID之后讨论)保持不变。显然,若是大规模出现行迁移的现象,那么必然会对数据库访问的效率产生严重影响(I/O大幅增长),若是表中发现大量行迁移现象,建议进行重组(REORG)操做,从新规划数据存储位置消除行溢出。

 

对应DB2数据页还须要强调的一点是:DB2的数据页和Oracle数据块同样,也有PCTFREE和PCTUSED的概念,其含义与做用也大体相同,不赘述。

 

另外能够看到,Oracle的数据块结构和DB2的数据页结构是很是类似的。还有一点,两者的行数据都是从高位开始向低位写,而行目录则是由低位向高位写,为何要这样呢?由于数据的插入是一个两头写的过程,既要将数据写入到可用空间中成为行数据或记录,又要更新头部后面的行目录或槽目录,若是同侧写不利于空间的扩展。下图是一个直观图:

 

数据行结构

如今对数据块(页)的具体结构已经有了一个大概的了解,那么表中的一行数据是怎样以行数据(记录)的形式保存在数据块(页)中的呢?这个问题看似简单,实际上,一个数据行(记录,即上图中的Row)的结构是很是复杂的,这里以DB2中数据行的结构为例。如图:

各参数解释以下:

能够看到,当元组(即表中的行)中存在变长列的时候,无论该列位于什么位置,在数据行(记录)中都被挪到最后面存储,而定长列则顺序存储。

【注:表的属性列在定义的时候指定了数据类型,有的数据类型是变长的,好比varchar,其大小随实际值的变化而变化,有的列数据类型的定长的,好比int为32位,物理该列上的实际值是多少,都占用4个字节。一旦列中出现变长数据类型的列,则该元组为变长元组。】

下图是状态位A的每一位的含义(1个字节=8位),状态位B是未使用的。

下面看看一个定长的元组(行)在数据行(记录)中具体是如何存放的:

有些复杂,将此图与前面贴的结构图对照着看。【注意:全部数据在数据页的数据行(记录)中都是以十六进制存放的,且为逆序存放】

下面是变长的元组(行)在数据行(记录)中具体存放方式,与定长元组的存放方式大同小异:

对于含大对象数据类型的元组,其大对象数据并不与数据行存放在一块儿,而是与数据行分开放置于数据库的不一样页中,在该元组(行)的数据行中存放一个指向该大对象的指针。如图:

 

以上就是数据库系统磁盘存储内部结构的大体内容了

 

 

https://blog.csdn.net/u011537073/article/details/49157903 ,若有侵权,请联系 linjie.rd@gmail.com 删除

相关文章
相关标签/搜索