SQL Server数据库损坏检测以及SQL Server数据库修复的解决方法
在一个理想的世界中,不会存在任何数据库的损坏,就像我们不会将一些严重意外情况列入我们生活中的日常一样,而一旦这类事情发生,一定会对我们的生活造成非常显著的影响,在sql server中也同样如此,或许几年内您没有遇见过数据库中出现这类情况,而一旦遇见这类情况,往往伴随着数据的丢失,宕机,严重甚至您本身的职业生涯也会受到影响。因此对于这类情况,我们需要了解数据库损坏方面的知识,以便我们能够事前准备,事后能够处理。本篇文章会对数据库损坏的原因、现象、事前和事后的一些处理方法以及简单的修复方法进行探讨。
数据库为什么会损坏?
在了解数据库损坏之前,首先我们要了解sql server是如何将数据保存到数据文件(mdf、ndf等)。无论更新还是插入数据,数据都需要首先在内存中的buffer pool驻留,然后通过checkpoint和lazy writer等过程将内存中的数据持久化到磁盘。在这个过程中,数据脏页由内存写入持久化的io子系统,在此期间,按照io子系统的不同,数据可能经过这几层:
windows(写数据一定调用的是windows api)
windows底层的中间层(杀毒软件,磁盘加密系统)
网卡、路由器、交换机、光钎、网线等(如果io子系统不是直连的话)
san控制器(如果使用了san)
raid控制器(io子系统做了raid)
磁盘或ssd等持久化存储器
因此,数据页被写入持久化存储期间,可能经过上述列表中的几项。在经历上述过程中,硬件环境会受到很多方面的影响,比如说电压是否稳定、断电、温度过高或过低、潮湿程度等,而软件方面,由于软件都是人写的,因此就可能存在bug,这些都可能导致数据页在传输过程中出现错误。
此外,影响磁盘的因素也包括电压是否稳定、灰尘等因素,这些也有可能引起磁盘坏道或整体损坏。
上面提到的所有因素都可以被归结为io子系统。因此,造成数据损坏的情况绝大部分是由io子系统引起的,还有非常非常小的概率内存芯片也会导致数据页损坏,但这部分情况微乎其微,因此不在本文的讨论之列。
上面提到的这些导致数据损坏的原因都属于天灾,还有一些人祸。比如说通过编辑器等手动编辑数据文件、数据库中还有需要redo和undo的事务时(也就是没有clean shutdown)删除了日志文件(通常会导致数据库质疑)。
发现数据库损坏
在我们知道可能造成数据库的损坏原因之后,接下来我们来看sql server是如何监测数据库页损坏的。
在sql server的数据库级别,可以设置页保护类型,一共有三个选项:none,checksum,torn_page_detection,如图1所示:
图1.页保护的三种选项
关于这三种选项,首先,请无视none,请不要在任何场景下选择该选项,该选项意味着sql server不对页进行保护。
其次是torn_page_detection,在sql server中,数据的最小单位是页,每一页是8k,但是对应磁盘上往往是16个512字节的扇区,如果一个页在写入持久化存储的过程中,只写了一半的页,这就是所谓的torn_page_detection,sql server通过每个扇区提512字节中前2位作为元数据,总共16个扇区32位4字节的元数据(页头中标识为:m_tornbits),通过该元数据来检测是否存在部分写的torn_page,但该类型的页验证无法检测出页中的写入错误,因此在sql server 2005及以上版本,尽量选择checksum。
在sql server 2005及以上版本,引入了checksum,checksum可以理解为校验和,当数据页被写入持久化存储时,会根据页的值计算出一个4字节的checksum存于页头(页头中标识同为:m_tornbits),和数据在同一页中一起保存在数据库中。当数据从io子系统被读取到内存中时,sql server会根据页内的值再次计算checksum,用该重新计算的checksum和页头中存储的checksum进行比对,如果比对失败,则sql server就会认为该页被损坏。
由checksum的过程可以看出,只有在页被写入sql server的过程中才会计算checksum,因此如果仅仅改变数据库选项的话,则页头中的该元数据并不会随之改变。
与io相关的三种错误
通过上述checksum的原理可以看出,sql server可以检测出页损坏,此时,具体的表现形式可能为下述三种错误的一种:
823错误,也就是所谓的硬io错误,可以理解为sql server希望读取页,而windows告诉sql server,无法读取到该页。
824错误,也就是所谓的软io错误,可以理解为sql server已经读取到该页,但通过计算checksum等值发现不匹配,因此sql server认为该页已经被损坏。
825错误,也就是所谓retry错误。
其中, 上述823和824错误都是错误等级为24的严重错误,因此会被记录在windows应用程序日志和sql server的错误日志中,而引起该错误的页会被记录在msdb.dbo.suspect_pages中。sql server错误日志中也会记录到出错页的编号,如图2所示。
图2.824错误在sql server错误记录中的描述
因此,如果我们存在完善的备份的话,我们可以通过备份进行页还原(在此再次强调一下对于dba来说,有”备”无患),一个简单的页还原代码如代码清单1所示。
use [master]
restore database [corrupt_db] page='1:155'
from disk = n'c:\xxx.bak'
with file = 1, norecovery, nounload, stats = 5
代码清单1.一个简单的页还原代码,从备份中还原文件id1中的第155页
记得我们前面说的,在读取页计算校验和时出错,这既可能是被写入持久化存储的页本身出错,也可能是在页被读取的过程中出错,此时sql server会尝试从io子系统中再次读取该页,最多可能是4次尝试,如果在4次尝试过程中校验和通过,则会是825错误,否则是824错误。这里要注意,与823和824错误不同的是,825错误是一个等级仅为10的信息。
因此,由于有固定的错误编号,因此可以在sql server agent中对823和824设置警报。
备份checksum
上述页checksum只有在页被使用时才会被校验页的正确性。在备份数据库时,可以指定checksum选项来使得备份读取的页也计算校验和,从而保证了被备份的数据库是没有损坏的。在图3的备份选项我们可以注意到这两条:
图3.checksum和continue_after_error选项
如果启用了checksum,当备份过程中发现了页校验和错误时,就会终止备份,而启用了continue_after_error选项的话,在检测到校验和错误时,仍然继续从而使得备份成功。
备份如果启用了checksum选项,除去检测每一页的校验和之外,还会在备份完成后,对整个备份计算校验和并存储于备份头中。
此外,对于备份,我们还可以通过restore verifyonly with checksum来验证备份,来保证备份的数据没有被损坏。
dbcc checkdb
前面提到sql server发现错误的方法有两种,分别为在读取页时和在备份时(本质上也是读取页)。但如果我们希望对于数据一致性的检查更加的激进,那我们应该定期使用checkdb来检查数据的一致性,而不至于在生产时间数据被读取时才能发现错误。
checkdb命令会对整个数据库做所有的一致性检查。当检查对象是master数据库时,checkdb还会检查resourcedb。
checkdb最简单的用法如代码清单2所示,在当前数据库上下文中直接执行checkdb,将会检查当前数据库中所有的一切。
dbcc checkdb
代码清单2.checkdb最简单的用法
checkdb命令在企业版中会使用多线程来进行,会对整个数据库进行一致性检查,在该过程中,使用了内建数据库快照的方式进行,因此不会造成阻塞,但checkdb会消耗大量的cpu、内存和io。因此checkdb要选择在维护窗口时间或是系统闲时进行。
默认情况下,checkdb命令会将输出所有的信息,但通常我们并不关心这些信息,而是只关心错误信息,因此实际中通常给dbcc指定不显式信息的参数,如代码清单3所示。
dbcc checkdb with no_infomsgs;
代码清单3.checkdb通常搭配no_infomsgs参数
实际上,checkdb是一套命令的汇总,checkdb会依次检查下述内容:
初次检查系统表
分配单元检查(dbcc checkalloc)
完整检查系统表
对所有表进行一致性逻辑检查(dbcc checktable)
元数据检查(dbcc checkcatalog)
ssb检查
索引视图、xml索引等检查
首先,当发现系统表损坏时,只能通过备份进行恢复(这也是为什么备份除tempdb之外的系统表非常重要)。其次,在一个大数据库中,做一次checkdb时间会非常长,维护窗口时间或系统闲时的时间可能无法cover这段时间,那么我们可以将checkdb的任务分散到checkalloc、dbcc checktable、dbcc checkcatalog这三个命令中。
推荐阅读
-
SQL Server数据库损坏检测以及SQL Server数据库修复的解决方法
-
SQL server无法禁用xx已将数据库存上下文更改成为master2002错误解决方法
-
sql server 2000数据库备份还原的图文教程
-
SQL Server数据库中的存储过程介绍
-
SQL Server 2005 还原数据库错误解决方法
-
SQL Server误区30日谈 第16天 数据的损坏和修复
-
多种SQL Server数据库备份方法的精细讲述教程
-
VS2008连接SQL Server数据库文件出错的解决方法
-
还原sql server数据库的处理方法
-
sql server 2012 数据库所有表里查找某字符串的方法