表空间是数据库系统中数据库逻辑结构与操作系统物理结构之间建立映射的重要存储结构,它作为数据库与实际存放数据的容器之间的中间层,用于指明数据库中数据的物理位置。任何数据库的创建都必须显式或隐式的为其指定表空间,且数据库中的所有数据都位于表空间中。
用户可以根据硬件环境以及成本等需求,通过指定建立在不同容器上的表空间来自由选择数据的物理存储位置。同时由于备份和恢复可以在表空间级别执行,用户能够进行更多粒度的备份恢复控制。
理解表空间前先理解容器。
容器(Container)
容器是物理存储设备,可以通过目录名,设备名或文件名进行标识。事实上,这也正是三种容器类型。设备容器(如磁带等)和文件容器被同等看待,通常直接将其理解为数据文件(磁盘存储内部结构中有介绍,数据文件中包含若干Extent。)。系统管理表空间只能使用目录容器,数据库管理表空间只能使用设备容器和文件容器。
容器被分配给某个表空间,单个表空间可以使用多个容器,但容器只能属于一个表空间。容器通常位于本地磁盘上,某些远程网络设备或文件也能作为表空间的容器,但由于网络延迟与可靠性方面的原因,远程容器对数据库安全与性能会造成影响,因此不建议使用远程容器。
DB2数据库系统的表空间有三种管理类型:
系统管理表空间(SMS,System-Managed Space)
SMS表空间由操作系统的文件系统管理器分配并管理。在这种表空间中,数据存储空间完全由操作系统管理,SMS表空间能够使用的唯一容器是目录容器,SMS表空间可以定义多个容器,目录容器可以根据需要增加大小,因此SMS表空间的大小是可以动态增加的。但是一旦SMS表空间创建,就不能再为表空间增加或删除容器了。SMS表空间中通常包含多个文件,这些文件代表了存储在文件系统空间中的表对象,比如表数据,表索引,表大对象都是单独占用一个或若干个文件的。一旦为表指定了SMS表空间,那么表中的数据就不允许分开存储,即表的常规数据,索引,大对象数据不能位于不同的表空间中。在DB2 V9之前的数据库版本中,创建数据库(创建数据库时如果不指定表空间则会默认创建3个表空间)或表空间的默认类型就是SMS表空间。另外,只有DB2数据库允许有系统管理表空间,Oracle数据库的表空间都是数据库管理的,不存在系统管理表空间。
举个SMS表空间的简单例子:创建一个以containers1目录为容器的SMS表空间MYSPACE。再创建一张表TEST,其常规数据,索引,大对象均位于MYSPACE表空间中(如果三者指定不同的表空间则表创建会失败,事实上,指定了常规数据的表空间为SMS表空间后,索引和大对象的表空间也就默认了)。表创建完成后,物理磁盘上的数据是这样的:
其中SQL00002.DAT数据文件存放表中的常规数据;SQL00002.DTR数据文件存放由于重组,表连接等产生的临时数据。SQL00002.INX数据文件存放表中的索引。还有LF后缀的存放LONG VARCHAR或LONG VARGRAPHIC数据,LB后缀的存放BLOB,CLOB和DBLOB数据,LBA后缀的存放LB后缀文件的分配和可用空间信息等。另外顺便说下图中的SQLTAG.NAM文件。事实上每一个容器都有一个TAG数据结构用于标识容器属于哪一个实例的哪一个数据库的哪一个表空间。对于DMS表空间的容器,TAG位于第一个Extent内,对于SMS表空间的容器,则是以SQLTAG.NAM文件的形式存在。
使用SMS表空间的每一个表都会在表空间的容器(也就是目录)下对应产生一系列的SQL*.DAT ,SQL*.INX等文件。表和其对应的文件的信息存放在SYSIBM.SYSTABLES这个系统编目表中,每个表对应一个FID(表所在的文件组编号)和TID(表所在的表空间编号),可以使用查询语句进行查询:
Select fid,tid, name from sysibm.systables where name='TEST';
数据库管理表空间(DMS,Database-Managed Space)
DMS表空间由数据库管理系统(DBMS)自己管理控制,本质上讲,这种类型的表空间是为了最大程度满足数据库管理器的需要而设计并实现的一种特定目的的文件系统。DMS表空间是由有限数量的容器所组成的,DMS表空间可以使用的容器有设备容器和文件容器,这些容器的空间都是预先分配的且不允许修改大小的,但是与SMS表空间不同的是,DMS表空间允许添加容器。也就是说,SMS表空间和DMS表空间分别通过扩大容器大小和增加容器数量的方式实现表空间大小的增加。DMS表空间创建时需要手动指定一个或多个容器。以文件为容器的表空间创建完以后就是一个单独的文件。使用DMS表空间的表的数据可以分开存储,即为常规数据,索引和大对象数据指定不同的DMS表空间。
DMS自动存储表空间(Automatic Storage DMS)
自动存储表空间不是真正意义上的独立类型的表空间。它是DMS存储的另外一种处理方法。DMS需要很多的维护操作,而自动存储器则是作为一种简化的空间管理手段,能够自动进行表空间的管理维护,它是DB2 V8.8.2中引入的概念,目前取代SMS成为默认的表空间类型。
三种表空间的对比
三种表空间在各方面的区别是非常多的,下表显示了一些主要的区别:
除了使用 SMS 表空间可以简化管理之外,SMS和DMS存储模型之间最显著的差异是表空间的最大大小。在使用 SMS 时,DBA 最多只能在表空间中放 64GB 的数据。将页面大小改为 32K,可以将这个限制扩大到 512GB,但代价是每个页面上的可用空间可能会更少。改为 DMS 模型会将表空间限制扩大到 2TB(4K 页面大小的情况下)。如果将页面大小改为 32K,可用空间可以增长到 16TB。尽管还有让表大小突破 64GB 限制的其他方法,但是最简单的方法可能是一开始就使用 DMS 表空间。(为什么会有这些限制后面介绍)
DMS与自动存储DMS
那么DMS和自动存储哪种方式更佳呢?自动存储允许 DBA 为数据库设置在创建所有表空间容器时可以使用的存储路径。DBA 不必显式地定义表空间的位置和大小,系统将自动地分配表空间。在 DB2 9 中,数据库在创建时将启用自动存储,除非 DBA 显式地覆盖这个设置。
启用自动存储的数据库有一个或多个相关联的存储路径。表空间可以定义为 “由自动存储进行管理”,它的容器由 DB2 根据这些存储路径进行分配。数据库只能在创建时启用自动存储。对于在最初没有启用自动存储的数据库,不能在以后启用这个特性。同样,对于在最初启用了自动存储的数据库,也不能在以后禁用这个特性。
下面的表总结了管理非自动存储和自动存储之间的一些差异。
引入自动存储模型的主要目的是简化 DMS 表空间的管理,同时保持其性能特征。有的时候 DBA 必须定义使用的表空间的所有特征,但是许多应用程序都会从自动存储提供的简化管理获益。
上述是按照管理方式的不同对数据库表空间类型进行了划分,事实上,根据用途的不同,表空间也可以划分为五种类型:
系统表空间(SYSCATSPACE)
系统表空间又称为系统编目表空间,DB2系统编目表是DB2数据库保存所有DB2对象元数据的地方,在Oracle数据库中,被称为数据字典。而系统编目表就存放在系统表空间中。系统表空间是数据库创建时自动创建的,且每个数据库必须有且仅有一个系统表空间,系统表空间被强制命名为SYSCATSPACE。系统表空间默认是SMS表空间,也可以显式指定为DMS表空间。
系统工具表空间(SYSTOOLSPACE)
系统工具表空间是供DB2管理工具和SQL管理例程使用的特定表空间,系统工具表空间不能被显式创建,只有第一次使用下面任一工具或过程时才会自动创建:ADMIN_COPY_SCHEMA 过程 ,ADMIN_DROP_SCHEMA 过程, 管理任务调度程序, 改变表笔记本, ALTOBJ 过程, 自动重组(包括 db.tb_reorg_req 运行状况指示器), 自动收集统计信息(包括 db.tb_runstats_req 运行状况指示器), 配置自动维护向导, db2look 命令, 设计顾问程序, GET_DBSIZE_INFO 过程, 存储管理工具,SYSINSTALLOBJECTS 过程。其中管理任务调度程序、ADMIN_COPY_SCHEMA 和 ADMIN_DROP_SCHEMA 过程的首次使用不会创建系统工具表空间,但是他们使用的是系统工具表空间。
用户表空间(USERSPACE)
用户表空间也是数据库创建时自动创建的,表空间名称为USERSPACE1,数据库中的用户表默认存放于这个表空间中,用户表空间是可选的,一个数据库可以有多个用户表空间。必须至少有一个用户表空间(没有用户表空间的话数据库无法存放用户数据)。用户表空间也可以是SMS表空间或DMS表空间,通常使用DMS表空间。
临时表空间(TEMPSPACE)
临时表空间也是数据库创建时自动创建的,数据库管理器使用临时表空间在执行SQL操作时存储临时数据,例如排序,表重组,索引创建以及表链接等操作所产生的中间表都由临时表空间存储,数据库必须至少有一个临时表空间,也可以有多个。创建数据库时默认创建的临时表空间名称为TEMPSPACE1,且为SMS表空间。但是这个表空间的名称可以是任意的,当另外的临时表空间被创建后,该默认临时表空间也可以被删除。(但必须保证数据库有一个临时表空间)。临时表空间也可以是DMS表空间。另外,DB2支持系统临时表空间和用户临时表空间两种类型,系统临时表空间必须存在,用户临时表空间可以有0个或多个,用来声明临时表。
除了根据管理和用途划分表空间类型,还可以根据容量将表空间划分为常规表空间和大型表空间。但是这里的常规表空间和大型表空间都是针对DMS表空间而言,SMS表空间大小上限还不及常规表空间。
行指针
首先考虑一个问题:逻辑上,数据以数据行(元组)的形式保存在数据库的表中,但物理上,根据数据库磁盘存储的知识(数据库深入学习笔记----磁盘存储内部结构),数据肯定是存储在数据文件上的,确切的说是存储在数据块(页)上。那么,数据库是如何根据表中的行寻址到物理磁盘上数据页中的数据呢?
Oracle和DB2的解决方案是使用一种新的数据结构:行指针(或行指示器),Oracle数据库中称为ROWID,DB2中称为RID。在实际的数据库表中,每张表都会附加一个特定的隐藏列,即行指针列,也就是说,每一行数据都有一个行指针属性,它指向该行数据在物理磁盘中的具体位置。实际上不管是Oracle还是DB2,行指针都是可以参与SQL查询的(毕竟是有效的行属性。Oracle中的ROWID可以直接当作属性进行查询,DB2中则需要使用rid函数查询RID)如下图是DB2数据库的RID格式:
常规表空间
在DB2 V9之前,RID具有4个字节(32位)长度,其中3个字节用于数据页寻址,最后1个字节用于数据页内槽号寻址(《数据库深入学习笔记----磁盘存储内部结构》介绍过,每一行数据都是一条记录,存储在数据页的数据存储空间里,每一条记录都对应槽目录中的一个槽号)。
由RID的结构我们就可以计算出数据页能够容纳的记录数(行数)和表空间的容量了:
因为一个RID只有1个字节(8位)槽号,所以,一个数据页存储的记录数的最大值为255条(2的8次方-1,为什么要减1?这是因为磁盘存储那篇文章已经讲过,有的数据页是会有一个可用空间控制记录(FSCR)的,所以需要预留)。(说明:8位能寻址的范围就是0-255,槽号编号就只能是0-255,如果记录再多,就无法被槽目录编号了,无法被寻址,数据存了也是白存。)
同理,可以根据3个字节的页号,得出一个表空间最多能容纳16777216(16M,2的24次)个数据页。那么,如果是一个数据页4KB的话,表空间大小就是16M*4KB=64GB了。如果一个数据页32KB的话,就是16M*32KB=512GB。(DB2表空间支持的页面大小有4KB,8KB,16KB和32KB四种,一个表空间只能使用一种大小的数据页)
可以想象,如果数据库表中的行长度(一行所占用的存储空间)太小,由于一个数据页理论上最多只能存储255行(实际上,每一页允许存储的记录数通常少于255条),那必然造成数据页空间的浪费,比如4KB页,长度为12B的行存满页面也只占用12B*255=3060B的空间,剩下的1036KB多的空间只能浪费。下表显示了页面空间被浪费前的最小行长度:
一旦表空间中满足了最大页限制,有以下三种方案可供选择:
1.在视图中把这些表连接起来(多个表空间中的表在视图中合在一起);
2.使用DB2的数据库分区功能(DPF,Database Partitioning Feature),横跨多个分区将数据进行组合;
3.使用范围分区表。
无论哪种方案都需要将一些数据进行迁移并可能对应用程序进行修改,这无疑是很繁琐的。
大型表空间
为了能够让数据页中容纳更多的行(记录)以及表空间中容纳更多的数据页,DB2 V9引入了一种新的行指针(RID,行指示器)格式。数据页标记由3个字节增加到4个字节,槽位由1个字节增加到2个字节,这种6字节的RID格式最终将表空间大小上限扩充到原来的32倍。即4KB页的表空间最大容量为2TB,32KB页的表空间最大容量为16TB。同时,每个数据页所能容纳的行数理论上也扩大为65000多行,但由于规定每一行的最小长度为12B左右,所以,实际上4KB页能容纳的最大行数为4KB/12KB=341行左右,32KB页则是2300行左右。
下表是4种页实际用于存储数据的空间大小和实际允许存放的行数:
这种RID格式很好的解决了表空间大小受限以及数据页空间浪费(不再受制于槽号,而是行长度)的问题。但同时也带来了管理上的挑战,比如备份和恢复。事实上,当表开始增长到TB级的时候,就应该考虑使用一些诸如表分区,数据库分区等技术来进行大型数据量的管理了。
使用旧的4字节RID格式的表空间就是常规表空间,使用新的6字节RID格式的表空间就是大型表空间了。DB2 V9中大型表空间是DMS表空间的默认类型,当然也可以显式的创建常规表空间(指明表空间为REGULAR)。很显然,SMS表空间是不可能支持大型表空间的。
支持6字节RID格式的表空间包括:系统临时表空间,用户临时表空间,用户常规表空间。也就是说,只有系统表空间是不能创建为大型表空间的。因为目前的编目表不足以达到能够及时保证较大的表空间大小的状态。
大型表空间在DB2 V9之前叫长型表空间,用于存储长型或 LOB 表列,以弥补常规表空间在处理长型或LOB数据上的不足。
常规表空间向大型表空间的迁移
DB2 V9不会自动将常规表空间升级为大型表空间,但是可以手动升级:ALTER TABLESPACE tablespacename CONVERT TO LARGE。
ALTER命令不会物理地改变表空间的结构,只是改变了编目方式以指示表空间可以支持6字节的RID格式。要注意的是:
1.执行LARGE升级后需要立即COMMIT WORK,进行事务提交,否则表空间上会持有排它锁,同时在该表空间中对表所做的其他工作不会继续执行,直到该锁解除为止。
2.一旦表空间被修改为LARGE,为了更好的利用6字节RID的优势,还需要继续数据重组和索引重组。如果不进行索引重组,那么先前存在的表将延续每页255行以及3字节数量的限制,因为索引使用的仍是旧的RID,无法索引到超出原范围的页。而数据重组(表重组)与索引重组的影响不同,索引重组影响表空间中页的数量,表重组影响的是页中存放的行数。
临时表空间分为系统临时表空间和用户临时表空间
系统临时表空间用来存储各种数据操作(排序、重组表、创建索引、连接表)中所需的内部临时数据,虽然可以创建任意多个系统临时表空间,但建议用户只使用大多数表所使用的页大小创建一个,默认系统临时表空间名为TEMPSPACE1。
用户临时表空间用来存储已说明全局临时表(已说明全局临时表存储的是应用程序临时数据)。用户临时表空间不是在数据库创建时默认创建的。
缓冲池(BufferPool)
对于数据库管这种对性能要求极高的系统而言,缓存自然也是不可少的。数据库中的缓存称为缓冲池,缓冲池存在的唯一目的就是提高数据库系统性能。
缓冲池本质上是分配给数据库管理器管理的一块内存空间,用于读写数据页。(包括表行和索引数据页。内存中表行数据页称为缓存表)由于从内存访问数据比从磁盘访问数据快得多(访问磁盘是典型的I/O操作,一次I/O大概耗时0.0125S),通过将部分常用或者需要预取的数据存放在缓冲池以便系统直接访问和操作可以减少磁盘I/O,合理的缓冲池空间和好的缓冲池页面替换算法(提高命中率)可以大大提高数据库系统的性能。
注意分配给DB2的内存空间不仅仅是缓冲池,还包括包缓存空间,日志缓存空间等等一些其他的内存空间,其中缓冲池占了DB2内存的大部分。
缓冲池大概实现
DB2缓冲池的结构和具体实现是非常复杂的,只知道一个大概的实现过程,其实也跟操作系统缓存差不多。
首次访问数据库表中的数据行时,由于缓冲池内没有数据页,数据库管理器会将磁盘上相应的数据页读入缓冲池中,每当向缓冲池添加新页时,都会为该页生成一个页面描述符(BPD)以标识该页并指明该页状态,描述符被存放在Database Heap中,删除页时会将其描述符一并删除,页面状态发生变化后会更新该页描述符。
对缓冲池中数据页进行操作时会根据操作类型为数据添加一个共享锁或排它锁以保证数据的同步,添加的锁被存放在Locklist中统一管理。同时将操作的SQL语句存储到包缓存(package Cache)中,根据包缓存的数据在日志缓冲区(Log Cache)中生成日志文件。当操作提交时,如果数据页没有被修改(干净页),则无需向磁盘回写数据页,如果数据页被修改(脏页),则需要将修改后的数据页回写到磁盘,替换原有页,,该脏页变成干净页。同时立即将相应的日志文件写入磁盘,以保证发生断电等突发事故时保证能够利用磁盘上的日志文件进行回滚(Undo)或重做(Redo)。【未提交确认的修改会回滚,已提交确认的修改会重做】
操作提交后,数据页所持有的共享锁或排它锁也相应解除。所有这些数据页在操作提交后都会继续保留在缓冲池中。当下次进行访问时,如果缓冲池中存在需要访问的数据页(命中),则直接对该页进行操作,并同样进行日志记录和数据页回写等操作。如果缓冲池中不存在需要访问的数据页(未命中),则需要先从磁盘将数据页读入缓冲池,重复以上过程。
如果需要将新的数据页从磁盘读入缓冲池,而缓冲池正好已经满了该怎么办呢?这个时候就要根据页面替换算法,选择将某些页从缓冲池删除以容纳新页。页面替换算法有很多种,DB2使用的是一种类似于最近最少使用(LRU)算法的时钟算法。
缓冲池和表空间的关系
一个缓冲池是与单个数据库相关联的,可以被多个表空间使用。一个数据库可以有多个缓冲池。数据库创建时会生成一个名为IBMDEFAULTBP的默认缓冲池。当考虑将缓冲池用于一个或多个表空间时,必须保证表空间页大小和缓冲池页大小对于缓冲池所“服务”的所有表空间而言都是一样的(和表空间一样,缓冲池中的页大小也分为4KB,8KB,16KB和32KB四种)。一个表空间只能使用一个缓冲池。