4.8 数据库的真面目
一个数据库由用户定义的空间构成,这些空间用来永久存储用户对象,例如表和索引。这些空间被分配在一个或多个操作系统文件中。
数据库被分成逻辑页面(每个页面8KB),并且在每个文件中,所有页面都被连续地从0到x编号,其中x是由文件的大小决定的。我们可以通过指定一个数据库ID、一个文件ID、一个页码来引用任何一个数据页。当我们使用ALTER DATABASE命令来扩大一个文件时,新的空间会被加到文件的末尾。也就是说,我们所扩大文件的新空间第一个数据页的页码是x+1。当我们使用DBCC SHRINKDATABASE或DBCC SHRINKFILE命令来收缩一个数据库时,将会从数据库中页码最高的页面(文件末尾)开始移除页面,并向页码较低的页面移动。这保证了一个文件中的页码总是连续的。
当我们使用CREATE DATABASE命令来创建一个新的数据库时,会有一个唯一的数据库ID(DBID),并且我们能够从sys.databases视图中看到一行关于这个新数据库的信息。从sys.databases视图返回的各行包含了每个数据库的基本信息,例如数据库的名称、DBID和创建日期,以及可以使用ALTER DATABASE命令进行设置的每一个数据库选项的值。本章后面部分会更加详细地讨论数据库的各个选项。
空间分配
一个数据库中的空间用来存储表和索引。空间管理的单位被称为区(extents)。一个区由8个逻辑上连续的页面组成(64KB的空间)。为了能够更有效地分配空间,SQL Server 2005不会为少量的数据向数据表分配整区的空间。SQL Server 2005有两种类型的区。
n 统一类型的区 这些区为单个对象所有,区中所有的8个数据页只能被所属对象使用。
n 混合类型的区 这些区能为最多8个对象共享。
SQL Server为新的表或索引从混合类型的区中分配页面。当该表或索引增长到8个页面时,以后所有的分配都使用统一类型的区。
当一张表或一个索引需要更多的空间时,SQL Server需要找到能够用来分配的空间。如果该表或索引整体仍然少于8个页面,SQL Server必须找到能够用来分配的混合类型区构成的空间。如果表或索引有8个页面或更大,SQL Server必须找到一个自由的统一类型的区。
SQL Server使用两种特殊类型的页面来记录哪些区已经被分配出去了,哪些类型(混合类型或统一类型)的区可供使用:
n 全局分配映射(Global Allocation Map,GAM)页面 这些页面记录了哪些区已经被分配并用作何种用途。一个GAM页面在它所覆盖空间里针对每一个区都有一个数据位。如果数据位为0,那么对应的区正在使用;如果该数据位为1,那么该区为自由区。一个GAM页面除了页面头部和其他一些需要记入的开销大概有8 000字节或者说64 000位空间可用,所以每个GAM页面可以覆盖64 000个区,也就是大约4GB的数据。这意味着一个文件的每4GB空间对应一个GAM页面。
n 共享全局分配映射(Shared Global Allocation Map,GAM)页面 这些页面记录了哪些区当前被用作混合类型的区,并且这些区需含有至少一个未使用的页面。就像一个GAM页面,每一个SGAM页面覆盖了大约64 000个区,也就是大约4GB的数据。一个SGAM页面在它所覆盖空间里针对每一个区都有一个数据位。如果数据位为1,那么对应的被使用的区为混合类型,并且该区有一些自由页面;如果数据位为0,那么对应的区不是一个混合类型的区,或者虽然是一个混合类型的区,但是所有的页面都已被使用了。
表4-2显示了基于每一个区当前的使用情况,在GAM和SGAM中该区所对应的比特位模式。
表4-2 在GAM和SGAM页面中的比特位设置
|
区的当前使用情况 |
GAM比特位设置 |
SGAM比特位设置 |
|
自由,未使用 |
1 |
0 |
|
统一类型或已全部使用的混合区 |
0 |
0 |
|
含有自由页面的混合区 |
0 |
1 |
如果SQL Server需要找到一个新的完全没有使用的区,那么它可以使用任何一个在GAM页面中对应的比特位值为1的区。如果SQL Server需要找到一个有着可用空间(有一个或多个自由页面)的混合类型的区,那么它可以寻找一个对应的GAM中的值为0、SGAM中的值为1的区。如果不存在有可用空间的混合类型的区,SQL Server会使用GAM页面来寻找一个全新的区并将其分配为混合类型的区,然后使用该区中的一页。如果根本没有自由区,那么这个文件已经满了。
SQL Server能够迅速地锁定一个文件中的GAM页面,因为它总是位于任何数据库文件的第三页上(页码为2)。SGAM页面是在第四页上(页码为3)。下一个GAM页面出现在第一个GAM页面(页码为2)以后的每511 230个页面中,并且下一个SGAM页面出现在第一个SGAM页面(页码为3)以后的每511 230个页面中。每一个数据库文件的页码为0的页面是文件头页面,并且每个文件仅有一页。页面1是页面自由空间页(Page Free Space,PFS),稍后会有对它的讨论。在第6章中,我们会进一步地讨论一张数据表中的单独数据页是什么样的。现在,由于正在讨论空间分配,我们将会研究如何跟踪哪一个数据页属于哪一张表。
索引分配映射(Index Allocation Map,IAM)页面在4 GB的区间中跟踪被一个分配单元所使用的区。一个分配单元就是一组页面,这些页面属于一个数据表或索引的单个分区。它由下面三种类型页面中的一种组成:含有常规的行内数据的页面、含有大型对象(Large Object,LOB)数据的页面和含有行溢出数据的页面。在第6章中当使用到三种页面中的每一种时,我们会对其进行讨论。
例如,一张在四个分区上的含有所有三种类型的数据(行内数据、LOB数据和行溢出数据)的表将会有至少12个IAM页面。单张IAM页面也是仅仅覆盖单个文件的4GB区间,所以如果分区跨越多个文件,那么就会有多个IAM页面,同时如果文件大小超过4GB,并且分区使用了一个4 GB区间以外的数据页,那么也将会有额外的IAM数据页。
一个IAM数据页包含一个页头(IAM页头),该页头包含有8个页面指针槽,还有一组比特位用来将一个范围内的区映射到一个文件,这个文件并不必一定就是IAM页面所在的那个文件。页头包含有在IAM映射范围内的第一个区的地址。8个页面指针槽可能包含指向某些属于相关对象页面的指针,这些对象被包含在混合类型的区中,对一个对象来说,只有第一个IAM页面含有这些指针的值。一旦一个对象占用的页面超过8个,它所有的区都会是统一类型的区——这意味着一个对象决不会需要超过8个指针来指向处于混合类型区中的页面。如果一张表中的数据行已被删除,该表实际上可以使用的指针数不到8个。比特位映射中的每一个比特位代表了该范围内的一个区,而不论该区是否被分配给了拥有该IAM的对象。如果一个比特位是打开的,那么在此范围内相关的区就是被分配给拥有IAM的对象的;如果一个比特位是关闭的,那么此范围内相关的区没有被分配给拥有该IAM的对象。
例如,如果在IAM中的第一个字节中的比特位模式是11 000 000,那么在IAM覆盖范围内的第一个和第二个区被分配给了拥有该IAM的对象,并且从第三个到第八个区都没有被分配给拥有该IAM的对象。
IAM页面在需要的时候被分配给每一个对象,并且位于数据库中的随机位置。每一个IAM页面覆盖的可能范围大约是512 000个页面。
称为sys.system_internals_allocation_units的内部系统视图有一个称为first_iam_page的列指向一个分配单元的第一个IAM页面。那个分配单元的所有IAM页面组成了一个链表,每个IAM页面含有一个指针指向该链表中的下一个页面。我们会在第6章中讨论对象数据存储时更加详细地讲述分配单元。
除了GAM、SGAM和IAM之外,一个数据库文件还有其他三种类型的专门分配页面。页面自由空间(Page Free Space,PFS)页面用来跟踪一个文件中每一个特定的页面是如何被使用的。一个文件中第二个页面(页码1)就是PFS页面,以后每8 088个页面再出现一次。我们会在第6章中对它们进行更多的讨论。第七个页面(页码6)被称为增量更改映射(Differential Changed Map,DCM)页面。它跟踪一个文件中的哪一个区在最新一次完全数据库备份以后被修改过。第八个页面(页码7)被称为批量更改映射
(Bulk Changed Map,BCM)页面,该页面当文件中的一个区在最小量或批量记日志操作中被使用时用到。我们会在第5章中讨论备份和还原操作内幕的时候对这两种类型的页面进行更多讨论。就像GAM和SGAM页面,DCM和BCM页面针对它们代表的文件区间中每一个区都有一个比特位相对应。这些页面的常规间距为511 230个页面。
检查数据库的一致性
DBCC在SQL Server 2000以前版本的SQL Server中所代表的意思是数据库一致性检查器(Database Consistency Checker)。然而,微软从Sybase公司获取了该产品的代码库以后,DBCC开始具备越来越多的功能,并最终超出了单纯的一致性检查的范围。例如,DBCC被用来收缩一个数据库或数据文件、清空数据或执行计划缓存。从SQL Server 2000开始,微软最终承认了这种演化,并且SQL Server 2000和SQL Server 2005中的联机丛书实际上将DBCC定义为数据库控制台命令(Database Console Command),并将该命令分为四种类型:校验类、维护类、信息类和其他类。
在本章中,我们将会讨论DBCC命令中实际上用来做数据库一致性检查的命令,也就是说,校验类命令。这些命令是CHECK命令:DBCC CHECKTABLE、DBCC CHECKDB、DBCC CHECKALLOC、DBCC CHECKFILEGROUP和DBCC CHECKCATALOG。还有两个分别是DBCC CHECKCONSTRAINT和DBCC CHECKIDENT,将在第7章中讨论。那时我们还会对一些表和索引维护类的DBCC命令进行讨论,例如DBCC CLEANTABLE和DBCC UPDATEUSAGE。在第7章中讨论索引时,我们会讨论DBCC INDEXDEFRAG命令和它在SQL Server 2005中的替代命令。
DBCC校验类命令中最全面的当属DBCC CHECKDB。下面是其完整的语法:
DBCC CHECKDB
[
[ ( 'database_name' | database_id | 0
[ , NOINDEX
| , { REPAIR_ALLOW_DATA_LOSS | REPAIR_FAST | REPAIR_REBUILD } ]
) ]
[ WITH
{
[ ALL_ERRORMSGS ]
[ , NO_INFOMSGS ]
[ , TABLOCK ]
[ , ESTIMATEONLY ]
[ , { PHYSICAL_ONLY | DATA_PURITY } ]
}
]
]
我们稍后将讨论DBCC CHECKDB命令的大多数选项。作为其操作的一部分,DBCC CHECKDB会按照下面的顺序运行所有其他的DBCC校验命令:
n DBCC CHECKALLOC运行在数据库上
DBCC CHECKALLOC校验在GAM、SGAM和IAM页面中维持的空间分配信息。我们可以认为DBCC
CHECKALLOC执行的是交叉引用检查,用以校验每一个GAM或SGAM所指示的已经分配的区真的被分配了,并且所有没有分配的区在GAM和SGAM中都被指示为没有被分配。
DBCC CHECKALLOC还为每一个分配单元校验IAM链表,包括在链表中的IAM页面之间链接的一致性。最后,DBCC CHECKALLOC校验所有标记为已分配给分配单元的区确实被分配了。
n DBCC CHECKTABLE运行在数据库中的每一张表和索引视图之上
DBCC CHECKTABLE在表的结构之上执行一套完整的检查,并且默认情况下这些检查同时是物理的也是逻辑的。通过指定DBCC命令的physical_only选项,我们可以排除逻辑检查内容从而只是校验页面和记录头部的物理结构。Physical_only选项的目的是提供一种对表的物理一致性和会损坏数据的一般硬件故障的轻量级检查。在SQL Server 2005中,完整运行DBCC CHECKTABLE可能会比在之前版本中运行该命令消耗的时间长得多。
校验索引视图通过从底层的SELECT语句定义来重构该视图的行集,并将结果与存储在索引视图中的数据相比较。SQL Server在两个行集之间执行两次left-anti-semi连接来确定一个行集中有的行在另一个行集也存在。
n DBCC CHECKCATALOG在数据库上运行 DBCC CHECKCATALOG在不同的元数据表之间执行超过50次的交叉验证。我们不能通过运行带有各种REPAIR选项的DBCC操作来修复它所发现的错误。在SQL Serve 2005以前,DBCC CHECKCATALOG没有被包括在DBCC CHECKDB操作中,从而需要单独运行。
n 验证数据库中的Service Broker数据 因为没有具体的DBCC命令来执行这项检查,所以运行这个命令是检查Service Broker数据的唯一方法。我们也可以认为DBCC CHECKFILEGROUP是DBCC CHECKDB的一个子集,因为DBCC CHECKFILEGROUP会在一个给定的文件组的所有表和视图上执行DBCC CHECKTABLE操作。
因为DBCC CHECKALLOC、DBCC CHECKTABLE和DBCC CHECKCATALOG命令都作为一部分被包含在DBCC CHECKDB中,所以如果DBCC CHECKDB定期运行的话就不必单独运行这些命令了。如果选择单独运行这些命令的其中之一,请参考联机丛书来获得这些命令的完整语法。
在一个升级而来的没有SQL Server 2005特性和索引视图的数据库上,DBCC CHECKDB实际上能比在对应的SQL Server 2000上运行稍快一些。然而在一个新的SQL Server 2005数据库上,为了补充SQL Server 2005的新特性而增加了一些逻辑检查,这必然会为其增加一些复杂度,同时也会增加其被调用后的运行时间,所以也许会发现DBCC CHECKDB会消耗比我们所预期的更多的时间。
执行校验检查
在SQL Server 2005中,所有的DBCC校验命令都会使用数据库快照技术来防止校验操作干扰正在进行的数据库操作,并允许校验操作能够看到所校验数据静止的、一致的视图,而不必理会该操作进行期间有多少更改发生在底层的数据之上。我们会在本章中对数据库快照进行更多讨论。在CHECK命令开始的时候会创建一个数据库的快照,并且所有被检查的数据上都不需要加锁。实际上检查操作是在快照之上进行的。
在讨论数据库快照时你会发现,当更新发生在数据源上时,一个数据页的原始版本被复制到快照数据库,所以快照总是能够反映数据的原始版本。不像定期的数据库快照,DBCC CHECKDB使用原始页面图像创建的“快照文件”对终端用户来讲并不可见,其位置也不能配置。当数据库被检查时,它总是使用同一个卷上的空间。这个功能只有当我们的数据目录在NTFS分区上时才能够使用。
如果没有使用NTFS,或者不想使用快照必须使用的空间,我们可以通过使用DBCC命令的WITH TABLOCK选项来避免创建快照。另外,如果我们使用了DBCC的REPAIR选项之一,由于数据库是在单用户模式下的,所以快照不会被创建,也没有其他事务能够更改数据。没有使用TABLOCK选项时,因为不会干扰数据库中其他正在进行的工作,DBCC校验命令被认为是在线操作。然而当使用TABLOCK选项时,当命令进行时需要获得它要处理的每张表上的一个共享表锁(Shared Table Lock),所以并发的修改操作会被阻塞。类似地,如果修改操作正在一张或多张表上进行,一个带有TABLOCK选项的DBCC校验命令将会被阻塞,直到执行修改的事务完成为止。
因为SQL Server需要临时存储在检查操作期间所观察到的关于页面和结构的信息,DBCC校验检查会请求大量的空间,这些空间是为了对在稍后的DBCC扫描中所观察到的页面和结构进行交叉检查。为了提前确定tempdb所需要的空间,我们可以运行带ESTIMATEONLY选项的DBCC校验检查。例如,如果我们想了解在AdventureWorks数据库上运行DBCC CHECKDB可能需要多少tempdb空间,可以运行下面的语句:
SET NOCOUNT ON;
DBCC CHECKDB ('AdventureWorks') WITH ESTIMATEONLY;
下面是我所收到的输出:
Estimated TEMPDB space needed for CHECKALLOC (KB)
-------------------------------------------------
72
Estimated TEMPDB space needed for CHECKTABLES (KB)
--------------------------------------------------
198542
注意虽然AdventureWorks被认为仅仅是一个示例数据库,但是完成运行DBCC CHECKDB也需要高达193MB的tempdb空间。AdventureWorks中有几个大型索引促成了这种大量的空间需求,另外,这个估计值是按照最坏的情况计算的,并且假定内存中没有空间可供任何所需要的排序操作使用。
SQL Server在启动页面里跟踪DBCC CHECKDB在每个数据库上最后一次成功的运行,当SQL Server启动时,它会将该操作的日期和时间记入错误日志(error log)。AdventureWorks数据库记入错误日志的信息看起来应该像下面的样子:
Date1/24/2006 2:15:52 PM
Message
DBCC CHECKDB (AdventureWorks) executed by TENAR\Administrator found 0 errors and repaired 0
errors. Elapsed time: 0 hours 5 minutes 8 seconds.
校验类检查
SQL Server 2005包含了一组逻辑性校验检查来验证数据与列的数据类型相匹配。这些检查可能非常消耗资源并且会影响系统的性能,所以我们可以选择使用PHYSICAL_ONLY选项来禁止这些检查和所有其他非核心的逻辑性校验。在SQL Server 2005中创建的所有新数据库在默认情况下都启用了DATA_PURITY逻辑性校验。对从之前SQL Server版本升级上来的数据库,我们必须运行一次带DATA_PURITY选项的DBCC CHECKDB,最好是在升级之后马上运行该命令,其语法如下:
DBCC CHECKDB (<db_name>) WITH DATA_PURITY
在数据库的纯净度检查成功完成之后,默认接下来DBCC CHECKDB执行的是逻辑性校验,并且没有办法改变这种默认行为。当然,我们可以使用PHYSICAL_ONLY选项来覆盖默认行为。但是使用这个选项不仅会跳过数据纯净度检查,还会跳过所有实际上必须分析的单个行数据内容,并且基本上将DBCC执行的检查限制在页面和行头物理结构的完整性方面。
如果数据库启用了CHECKSUM选项,所有的SQL Server 2005新数据库默认情况下就启用了该选项,该选项将会在每一个分配的页面被DBCC CHECK命令读取时对其执行求校验和操作。接下来的关于数据库选项的一节里,我们会再次提到,在CHECKSUM选项启用的情况下,当每一个页面被写入磁盘时,页面的校验和会被计算出来并写在页面上,所以只有在CHECKSUM被启用后写入的页面才能够进行这种检查。页面的校验和值在读取期间会被检查并与存储在页面上的原始校验和相比较。如果它们不匹配的话,就会产生一个错误。另外,有错误的页面被存储在msdb数据库的suspect_pages表中。
DBCC Repair 选项
校验类命令DBCC CHECKDB、DBCC CHECKTABLE和 DBCC CHECKALLOC允许我们指定是否需要试图修复可能发现的错误。DBCC校验命令的语法(除了DBCC CHECKCATALOG)允许我们指定REPAIR_ALLOW_DATA_LOSS或REPAIR_ REBUILD选项。
从语法上来讲我们还可以指定REPAIR_FAST选项,但是该选项的继续存在只是为了维持后向兼容性,并且不会执行任何实际修复操作。
几乎所有DBCC命令能够探测到的可能的错误都可以被修复。例外情况是通过DBCC CHECKCATALOG发现的错误,以及通过DBCC CHECKTABLE命令发现的数据纯净度错误。当我们运行带一个REPAIR选项的DBCC CHECKDB命令时,SQL Server首先运行DBCC CHECKALLOC并修复它所能修复的错误,然后在所有的表上运行DBCC CHECKTABLE命令,并在这些表上作适当的修复。为了让整个DBCC操作能够最有效地运行,当SQL Server编辑需要修复内容的列表时,会对每张表可能的修复分级。这样可以使得该操作不会以失败告终。例如,一种情况是当前正在重建一个索引,并且之后必须从表中移除一个数据页面,这将使得之前重建索引的工作无效。
如果我们运行带有REPAIR_ALLOW_DATA_LOSS的DBCC命令,即使冒着丢失数据的风险,SQL Server也会试图修复探测到的几乎所有错误。需要注意的是,对几乎所有的服务器错误运行修复时都会丢失一些数据。在修复期间当发现一些行不一致时(例如当某个计算列的值不正确时),会删除这些行。当发现校验和错误时,一些页面会被完全删除。在修复期间,不会试图维护表上或表间的任何约束。对某些错误,SQL Server甚至不会试图去修复——特别是当GAM或SGAM页面自身损坏或不可读时。
当我们使用REPAIR_REBUID选项时,SQL Server会执行较小的相对较快的修复行为。例如:修复在非聚集索引里的额外键值,也会执行耗时较多的修复,例如重建索引。执行这些类型的修复不会有丢失数据的风险。在DBCC命令成功完成后,数据库在物理上是一致的并且在线,但在约束和业务规则方面也许并不是逻辑一致的。因此,使用REPAIR应该只是用作最后的手段。对不可修复的错误更好的一种解决方案是从备份还原数据库,或者还原数据库的一小部分,例如单个的文件组。如果我们打算使用REPAIR_ALLOW_DATALOSS选项,那么应该在执行前先备份相应的数据库。
我们可以在用户定义的事务中运行REPAIR选项,这意味着我们能够进行回滚(ROLLBACK)来撤销已经执行的修复。例外情况是,在一个处于紧急(EMERGENCY)状态的数据库上运行REPAIR选项时,我们会在稍后的数据库选项一节来讨论这个问题。(如果在EMERGERCY模式下的修复失败了,那么除了从备份还原数据库以外没有别的选择。)除非后续的修复依赖于之前修复的成功,DBCC操作中每一个单独的修复都在自己的系统事务中运行。如果我们确实运行了一个REPAIR选项,通过在修复初始化之前创建一个数据库快照,开始一个事务,然后再运行带有REPAIR选项的DBCC命令这
种方法能够提供部分的安全保证。在提交事务或回滚事务前,我们可以比较修复后的数据库和原始的快照。 如果对作为修复一部分并已经作出的修改不满意,我们可以回滚该修复操作。
进度报告
SQL Server 2005中的很多DBCC命令在名为sys.dm_exec_requests的动态管理视图中提供了进度报告功能。请看下面的这些列:
n command-indicates 当前的DBCC命令状态。
n percent_complete-represents [%] DBCC命令的完成状态。
n estimated_completion_time (单位为毫秒)——代表了基于过去的进度 所估计的完成当前任务需要的时间。
DBCC CHECKDB、DBCC CHECKTABLE和DBCC CHECKFILEGROUP都具有进度报告功能。没有包括DBCC CHECKALLOC是因为这个命令可以很快完成,所以不需要也来不及检查其进度。该命令在我们有机会查询视图sys.dm_exec_requests之前就已被完成了。一些维护命令也具有进度报告功能,例如DBCC SHRINKFILE和DBCC SHRINKDATABASE。当使用带REORG选项的ALTER INDEX命令来对索引进行碎片整理时,SQL Server也会通过进度报告的各列来显示命令执行的进度,因为这个命令与提供进度报告的DBCC INDEXDEFRAG命令等价。
DBCC 最佳使用方法
当我们在规划使用DBCC校验命令的时间和方式时,可以考虑以下几点:
n 使用带CHECKSUM数据库选项的CHECKDB命令和一个好的备份策略来保护数据的完整性不受到硬件故障的损坏。
n 并没有一定要遵守的规则来规定必须多久运行一次DBCC——那取决于数据的重要性、硬件的质量和备份的频率。
n 在将一个数据库升级到SQL Server 2005以后,执行带DATA_PURITY选项的DBCC CHECKDB命令来检查是否有无效的数据值。
n 确保有足够的磁盘空间以容纳将来创建的数据库镜像。
n 确保tempdb中有可用空间供DBCC 命令运行。注意:我们可以使用ESTIMATEONLY选项来估计DBCC CHECKDB、DBCC CHECKTABLE、 DBCC CHECKFILEGROUP和DBCC CHECKALLOC等命令需要多少tempdb空间。
警告: 只有在最后一步再考虑使用REPAIR_ALLOW_DATALOSS选项。




