DBCC CHECKDB指令可以完成两项任务:(1)检查数据库里有没有损坏发生。(2)尽力修复数据库损坏,使数据能够重新被正常访问。所以哪怕是一个正常运行的数据库,也建议定期运行这句指令,以确保没有损坏发生。对于已经发生访问错误的数据库,应该在第一时间运行这句指令,了解损坏的范围和程度。 那么DBCCCHECKDB究竟做了哪些检查呢?
在做些什么 1. 检查一些关键的系统表。 2. 对数据库运行DBCC CHECKALLOC。 3. 对数据库中的每个表和视图运行DBCC CHECKTABLE。 4. 对数据库运行DBCC CHECKCATALOG。 5. 验证数据库中每个索引视图的内容。 6. 验证数据库中的Service Broker数据。 这意味着运行了DBCC CHECKDB,就不必再单独运行DBCC CHECKALLOC、DBCCCHECKTABLE或DBCC CHECKCATALOG命令。也意味着单独运行DBCCCHECKALLOC、DBCC CHECKTABLE和DBCCCHECKCATALOG命令,虽然不能完全完成DBCC CHECKDB的所有功能,但是至少完成了大部分功能。
检查一些关键系统表 关键系统表有: sysallocunits。 syshobts。 syshobtcolumnes。 sysrowsets。 sysrowsetcolumns。 对于普通用户访问,这些表都是不可见的。只有在DAC模式下的连接,才能看到它们。它们的结构对普通用户也是透明的。 这里的每一张系统表都有一个聚集索引。SQLServer会像做CHECKTABLE一样,对这些系统表做一遍检查,确保这些表格里的每一页面及页面里的每一条数据都可以正确地读出来。如果中间发现问题,例如发现某个页面不能被正常访问,SQLServer就会报错。例如: Server: Msg 8966, Level 16, State 1, Line 1 Could not read and latch page (1:33245) with latch type SH.Sysobjects failed. 对于小的数据库,这些存放元数据库的系统表不会占用太多的页面。如果发生硬件问题,损坏的几率比较小。但是对一些有成千上万对象的数据库,这些原数据系统表本身就可能使用了很多页面,发生损坏的几率也随之增大。由于这些系统表是正确读取一切数据的根本,所以如果任意一张系统表上发生了损坏,DBCCCHECKDB都会直接失败,数据库也无法做任何修复。此时恢复数据库的唯一方法,只有恢复数据库的备份。
对数据库运行DBCC CHECKALLOC DBCC CHECKALLOC将返回以下结果集(值可能有所不同,我们以AdventureWorks数据库为范例)。 DBCC results for 'AdventureWorks'. *************************************************************** Table sys.sysrowsetcolumns Object ID 4. Index ID 1, partition ID 262144, alloc unit ID 262144 (type In-rowdata). FirstIAM (1:139). Root (1:66). Dpages 0. Index ID 1, partition ID 262144, alloc unit ID 262144 (type In-rowdata). 13 pages used in 1 dedicated extents. Total number of extents is 1. *************************************************************** '...' *************************************************************** Table Production.TransactionHistoryArchive Object ID 158623608. Index ID 1, partition ID 72057594047037440, alloc unit ID72057594053459968 (type In-row data). FirstIAM (1:804). Root (1:6568). Dpages0. Index ID 1, partition ID 72057594047037440, alloc unit ID72057594053459968 (type In-row data). 622 pages used in 79 dedicated extents. Index ID 2, partition ID 72057594052804608, alloc unit ID72057594059227136 (type In-row data). FirstIAM (1:2897). Root (1:12608). Dpages122. Index ID 2, partition ID 72057594052804608, alloc unit ID72057594059227136 (type In-row data). 124 pages used in 17 dedicated extents. Index ID 3, partition ID 72057594052870144, alloc unit ID72057594059292672 (type In-row data). FirstIAM (1:2899). Root (1:12832). Dpages166. Index ID 3, partition ID 72057594052870144, alloc unit ID72057594059292672 (type In-row data). 168 pages used in 22 dedicated extents. Total number of extents is 118. --对于每一个表格对象,检查它每个索引上的每个分区的数据分配情况 '...' Processed 317 entries insystem catalog for database ID 5. File 1. The number ofextents = 2898, used pages = 22552, and reserved pages = 23179. File 1 (number of mixed extents =104, mixed pages = 827). Object ID 4, index ID 1, partition ID262144, alloc unit ID 262144 (type In-row data), data extents 1, pages 13,mixed extent pages 9. '...' --对于每个数据文件,检查上面的数据页面分配情况 The total number of extents= 2898, used pages = 22552, and reserved pages = 23179 in this database. (number of mixed extents = 104, mixedpages = 827) in this database. CHECKALLOC found 0allocation errors and 0 consistency errors in database 'AdventureWorks'. DBCC execution completed.If DBCC printed error messages, contact your system administrator. -- 对于整个数据库的总结
对数据库中的每个表和视图运行DBCC CHECKTABLE · 是否已正确链接索引、行内、LOB及行溢出数据页。 · 索引是否按照正确的顺序排列。 · 各指针是否一致。 · 每页上的数据是否合理(包括计算列)。 · 页面偏移量是否合理。 · 基表的每一行是否在每个非聚集索引中具有匹配的行,以及非聚集索引的每一行是否在基表中具有匹配的行。 · 已分区表或索引的每一行是否都位于正确的分区中。 DBCC CHECKTABLE返回以下正确结果集。 DBCC results for 'HumanResources.Employee'. There are 288 rows in 13 pages for object 'Employee'. DBCC execution completed. If DBCC printed error messages, contactyour system administrator.
对数据库运行DBCC CHECKCATALOG Attribute (object_id=130099504)of row (object_id=130099504,column_id=1) in sys.columns does not have a matching row (object_id=130099504) in sys.objects. --在sys.columns视图里,有一个列不属于sys.objects里的任何一个表格或视图
Attribute (referenced_object_id=130099504,key_index_id=2) of row (object_id=1447012236)in sys.foreign_keysdoes not have a matching row (object_id=130099504,index_id=2) in sys.indexes. --在外键视图sys.foreign_keys里的一个外键在sys.indexes里找不到对应的索引
Attribute (parent_object_id=130099504) of row (object_id=2061966422) in sys.objects does not have a matchingrow (object_id=130099504) insys.objects. --某个对象的父对象不存在。(例如有一个主键对象存在,但是主键所依附的表格不存在了。) 这些错误在正常的数据库里不应该出现,除非用户自己去直接修改了系统表里的数据。
验证数据库中每个索引视图的内容 DBCC CHECKDB会将视图再计算一遍,验证索引页面里存储的字段值都是正确的,从而保证索引视图的数据可靠性。但是如果视图查询的源数据量非常大,CHECKDB会使用tempdb里的空间协助计算。这个过程会比较消耗资源和时间。如果想要跳过这一步,可以使用PHYSICAL_ONLY这个参数。 由于索引页里的数据都是根据源数据计算出来的,如果CHECKDB发现有任何问题,修复起来还是比较容易的。只要把现有的有错误的数据删除,新计算一遍重新存储起来就可以。所以索引视图上的问题一般不会导致数据丢失。 对于XML字段上建立的索引,SQL Server在这一步也会做类似的验证。
验证数据库中的Service Broker数据 最后,DBCCCHECKDB会打印出一句总结的话: CHECKDB found 0allocation errors and 0 consistency errors in database 'AdventureWorks'. 如果您的数据库没有说“0allocation errors”和“0 consistency errors”,而是有若干个错误,那就意味着数据库有损坏了,赶紧修吧。
提供的修复方法
· REPAIR_ALLOW_DATA_LOSS
· REPAIR_FAST
· REPAIR_REBUILD 如果已通过使用ALTERDATABASE语句将数据库设置为紧急模式,那么假如指定了REPAIR_ALLOW_DATA_LOSS选项,则DBCC CHECKDB可以对数据库执行某些特殊修复,恢复的数据库在物理层面的一致性,使其能够重新访问。但是这种修复是以丢失数据为前提的。所以应当是最后手段,并只有在无法从备份还原数据库时才采用。 将数据库设成紧急模式并以REPAIR_ALLOW_DATA_LOSS子句运行DBCC CHECKDB时,将执行以下操作: · 将由于I/O或校验错误而被标记为不可访问的页重新标记为可访问,就如同这些错误没有出现过一样。这样用户将能够访问这些页面,虽然页面里的内容肯定有问题。 · 将尝试使用常规的基于日志的恢复技术恢复数据库。 · 如果由于事务日志损坏而导致数据库恢复失败,则将重建事务日志。但是重建事务日志可能会导致数据库里的事务不一致。 如果DBCCCHECKDB命令成功,则说明数据库在物理结构上是正确的,并且数据库状态将设置为ONLINE,用户可以正常访问。但是,数据库可能包含一种或多种事务不一致的情况。修复操作也不会考虑表本身或表之间可能存在的任何约束。如果指定的表与一个或多个约束有关,建议在修复操作后运行DBCCCHECKCONSTRAINTS。如果必须使用REPAIR,则运行不带有修复选项的DBCCCHECKDB来查找要使用的修复级别。如果必须使用REPAIR_ALLOW_DATA_LOSS级别,则建议在运行带有此选项的DBCC CHECKDB之前备份数据库。因为DBCCCHECKDB造成的修改,用户可能会无法接受,而这个时候也没办法再用备份恢复的方法恢复数据库了。 对于REPAIR_ALLOW_DATA_LOSS,这里的data loss不仅仅是指一些数据记录可能被删除,还指有些错误的记录会保留在数据库里,让用户继续使用。所以其实这是一种比较危险的选择,只能在万不得已的时候选用。 如果一个数据库使用REPAIR_ALLOW_DATA_LOSS级别都不能修复,管理员又能怎么办呢?选择真的十分有限。以下是可用的几种方法: (1)按照预先的备份恢复策略,恢复数据库备份。 这个可以说是最好的办法,能够将数据库恢复到一个一致的时间点。强烈建议管理员使用这个选择。 (2)如果损坏发生在某些用户对象上(用户表、视图、存储过程等),可以把它们DROP掉试试。 (3)将数据库设成紧急只读模式,用“SELECT ... INTO”或其他方式,将数据导入到一个新建的空数据库里。 设置紧急模式的指令是: ALTER DATABASE <DB_Name>SET EMERGENCY 这种方法能够从数据库中将所有能读出来的数据都读出来,挽救尽可能多的数据。但是损坏严重程度不一样,丢的数据多少也不一样。这样救回来的数据库各个数据表的状态将会不一致,一般在逻辑上会有很大的问题。 还是老生常谈,恢复备份永远是最好的选择。
首先,SQL Server作为一个能够支持超大数据库的数据库管理系统,在DBCC CHECKDB上不断地改进着。在现在的应用中,数百GB以至上TB的SQL Server数据库比比皆是。不是说有用户在使用数据库,就不能运行CHECKDB的。SQL Server通过下面这些技术大大提高了CHECKDB的速度,降低了它对并发用户的影响,防止了阻塞。
内部数据库快照
并行检查对象 并行度其实是一把双刃剑。越多的处理器在同时进行数据库检查,CHECKDB能够完成得越快。可是在这段时间里,就会有越多的系统资源被使用在CHECKDB上。其他同时在运行的连接可能就拿不到足够的资源,性能反而会受到更大的影响。所以管理员需要根据具体情况,在两者之间做一个平衡。决定是尽快完成CHECKDB更重要呢,还是要兼顾其他连接的性能。通过使用跟踪标志2528,可以禁用并行检查。
PHYSICAL_ONLY 既然有了这些技术,那么现在SQLServer运行一次CHECKDB大概要多久呢?很不幸,这真是一个非常难以回答的问题。运行的时间和下面这些因素有关系。 (1)数据库自身大小。 (2)当前系统I/O子系统的读写能力与繁忙程度。 CHECKDB需要把数据库从头到尾通读一遍。这本身就是一个巨大的I/O工作。I/O子系统能多快地完成SQL Server提出的读写请求,是影响到CHECKDB速度的最重要的因素。 (3)当前系统CPU负荷。 CHECKDB本身也是个验证工作。让计算机去验证什么事情,就意味着让CPU去做计算。所以有时候CHECKDB也会导致系统CPU负载加重。 (4)当前数据库的并发修改量。 虽然SQL Server使用快照技术大大提高了数据库运行CHECKDB时候的并发度,但是如果数据库上的修改非常频繁,快照数据库的维护本身会是一个很耗资源的任务,会反过来影响CHECKDB的速度。 (5)存放tempdb磁盘的速度。 当数据库非常大的时候,SQLServer需要很多内存来存放一些中间结果。有时候仅放在内存里是不合适也是不可能的,需要借助tempdb里的空间。那么tempdb磁盘的读写速度对CHECKDB的速度也会有显著影响。 (6)数据库里对象的类型。 不同类型的对象,需要验证其正确性所花费的代价是不一样的。比较费资源的对象有非聚集索引、计算列(computed column)、off-rowLOB values、Service Broker、XML索引、索引视图(indexed view)等。如果这些对象在一个数据库里使用得比较多,那么同样的大小,这样的数据库CHECKDB会更耗时一些。 (7)CHECKDB使用的参数。 有些参数可以让SQL Server少做一些检查。例如WITH NOINDEX可以让SQL Server不用去做费时费力的非聚集索引检查。WITH PHYSICAL_ONLY可以让SQLServer只做物理结构完整性检查等。 (8)数据库里面的错误类型和错误的数目。 如果SQL Server在CHECKDB的过程中发现了错误,它就会用更加复杂的算法来衡量错误的性质、范围和影响,以便将最准确有用的信息返回。这会使得CHECKDB的时间大大延长。 根据2012年时的经验,一个大于1 TB的数据库如果没有错误,CHECKDB在有些机器上用8小时就能够跑完。而一个有成百上千错误的数据库,哪怕只有两三百GB,也有可能一天都跑不完。这个区别是很显著的。 虽然比较难以估计做一个DBCCCHECKDB需要多长时间,但是读者可以通过下面的语句,查询做DBCC CHECKDB进度如何。有时候进度可能不太精确,但是我们至少心里有数大概什么时候能够做完。 SELECT session_id,request_id, percent_complete, estimated_completion_time , DATEADD(ms,estimated_completion_time,GETDATE())AS EstimatedEndTime, start_time, status,command FROM sys.dm_exec_requests WHERE command like '%DBCC%'
超大数据库上的最佳实践 如果数据库里设计了分区表(partitionedtable)机制,做起来可能比较简单一些。对于存储历史数据的分区文件组,由于数据本身已经不会发生修改,我们可以把文件组类型设成只读模式,防止任何误修改。每个月或者每两个星期对它们运行一次DBCCCHECKFILEGROUP即可。对于当前的数据,由于随时都可能发生读写,发生损坏的几率也高得多。所以可以每个星期,甚至一个星期两次,单独做DBCCCHECKFILEGROUP。 如果数据库没有分区机制,CHECKDB就是一个宏大的工程了。几乎可以说,没有机会让管理员找到一个时间窗口能把一个完整的CHECKDB做完。这时候怎么做呢? 有一种折中的方法,是把数据库里面的所有表格按照它们占用的页面数量大致分为7组,每组的页面数目大致一样。然后按照这样的方式把CHECKDB里面的关键任务分散在每天运行。
周一到周三:
周四:
周五周六:
周日: 用这种方法,可以在代价比较小的前提下完成CHECKDB的大部分工作。TB级数据库的管理员可以考虑试试。 如果您有一台备用服务器,则可以考虑不在生产环境上运行DBCCCHECKDB。而是把生产环境的数据库通过备份方式,还原到备用服务器上,在备用服务器运行DBCCCHECKDB。如果备用服务器上检查结果数据是完整的,这说明在对生产环境数据库做备份的那一刻,数据是完整的。如果备用服务器上执行的结果显示数据完整性有问题,则一定要在生产环境再次运行DBCCCHECKDB,以明确问题到底出在哪个环节。是生产环境的数据已经有问题,还是还原到备用服务器时出问题。这时候检查数据完整性的优先级高于性能影响。所以在生产环境再次运行DBCCCHECKDB是必须的。使用这种方法的缺点是,需要额外一台服务器,以及比较完善的自动化执行方案。 (责任编辑:熊猫蜀黍) |