欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  数据库

SQL Server数据库损坏检测

程序员文章站 2022-05-26 16:59:23
...

在一个理想的世界中,不会存在任何数据库的损坏,就像我们不会将一些严重意外情况列入我们生活中的日常一样,而一旦这类事情发生,一定会对我们的生活造成非常显著的影响,在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所示:

SQL Server数据库损坏检测

  图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所示。

SQL Server数据库损坏检测

  图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