■ 河南 刘景云
某单位网站后台使用的SQL Server 2016数据库,近来出现运行故障。
SQL Server提示出现编号为824,级别24的错误,检测到基于一致性的逻辑I/O错误pageid不正确(应为 10:910,但实际为 0:0)。在文件 'G:dataxxx.ndf' 中,偏移量为0x00000000720000的位置对数据库ID 5中的页(10:910)执行读取期间发生了该错误。
SQL Server错误日志或系统事件日志中的其他消息可能提供了更详细信息,这是一个威胁数据库完整性的严重错误条件,必须立即纠正。执行完整的数据库一致性检查(DBCC CHECKDB),此错误可以由许多因素导致。
根据以上提示信息,可以看出这是一个典型错误,说明SQL Server中的某些页出现了错误,这导致了数据无法正常访问的问题。
在进行修复之前,需要了解页的损坏情况,如果损坏比较严重,那么页还原是没有什么效果的。如果页损坏并不严重(一般在10个以内),那么恢复效果就比较好。
在正常情况下,数据库的页不会无端损坏,之所以出现页损坏,根本原因是存储出现了问题,例如硬盘出现坏道等。对于这种情况,首先需要检查磁盘状态,执行数据库的备份操作,并将备份数据保存到安全的位置。
对页损坏情况要进行严密监控,如果其持续发生,就必须立即更换存储设备。想了解页损坏情况,可以在Microsoft SQL Server Management Studio中选择目标数据库,在工具栏上点击“新建查询”按钮,执行“dbcc checkdb”命令,检查该数据库的页情况。如果数据库比较大,检测时间就会较长,并导致IO繁忙影响到用户的使用速度。因此,最好在维护窗口中进行检查。当检测完毕,会显示发现的错误信息,并且以红色表示页损坏信息(包括页编号等)。
也可以执行“select *from msdb.dbo.suspect_pages”命令。利用指定的系统视图,来快速检测页损坏情况。执行“dbcc ind('websitedb','product',1);”命令,用于定位到表或索引使用的Page信息,其中的“websitedb”表示网站数据库名称,“product”表示其中某张表的名称,“1”参数表示表的聚集索引。
要想查看页中的数据,需要先执行“dbcc traceon(3604);”命令,来打开指定的跟踪代码。之后才可以执行“dbcc page('websitedb ',1,910,3);”命令,来查看指定的页中的数据,这里的页号为“910”具体页号可以从错误提示信息中得到。
除了使用上述检测方法外,还可以使用“EXEC sys.sp_helpdb @dbname=pratice”,“USE pratice”,“XEC sys.sp_helpfile”命令,得到目标数据库的ID和数据库文件ID。执行“DBCC TRACEON(3604,-1)”,“DBCC PAGE(16,1,10,3)”“DBCC PAGE(16,1,910,3)”命令,分别检测发生问题的数据页面和索引页面,其中的“16”位目标数据库的ID,“10”和“910”位具体的索引页面和数据页面,根据返回信息,可以查看对应页面详细信息。
因为事先管理员对数据库进行了完整备份,所以修复起来就比较轻松了。SQL Server页还原需要使用完整恢复模式,在执行页修复时,需要遵循一定的步骤。
首先从完整备份来还原页,注意要指定页编号。之后应用最近的差异备份和后续日志备份,这几个步骤和日常的还原没有区别。接下来备份当前日志,目的是重做事务日志序列号LSN,最后还原当前日志。
首先执行“Restore headeronly from disk='E:DataWebsitedb.bak';”命令,检测备份文件的详细信息。例如,在其中的“BackupStartDate”和“BackupFinishDate”列中显示备份的时间点,如果起先于页损坏的话,就可以用来进行修复。执行“RESTORE DATABASE websitedb PAGE='10:910' FROM disk=' E:DataWebsitedb.bak ' WITH FILE=1,NORECOVERY;”命令,执行恢复备份文件操作,其中“E:DataWebsitedb.bak”为备份文件路径。
然后执行“RESTORE LOG websitedb FROM disk='E:DataWebsitedb.bak 'WITH FILE=2,NORECOVERY;”命令,执行日志还原操作。执行“BACKUP LOG websitedb TO disk='E:Data WebsiteTail.trn'--WITH NO_TRUNCATE;”命令,重做事务日志序列号LSN。执行“RESTORE LOG websitedb FROM disk=' E:Data WebsiteTail.trn 'WITH FILE=1,RECOVERY;”,“Go”命令,还原当前日志。这样,就修复了损坏的页。
如果没有完整备份,那么恢复起来就比较繁琐了。如果仅仅是索引页出现问题,可以先Drop索引,之后再创建索引即可修复,这不会造成任何数据损失。如果是数据页出现问题,就需要确定发生错误的页面属于哪一个数据表,可以执行“SELECT s.name AS N'架构名' o.name AS N'表名'FROM sys.sysobjects o INNER JOIN sys.schemas s ON o.uid=s.schema_id WHERE o.id=xxx”命令,来找到对应的数据表。
“xxx”为“ObjectID”的值,在执行上述DBCC PAGE(16,1,10,3)”命令时,在返回信息中的“Metadata:ObjectId=”会显示具体数值。找到目标数据表后,即可将内容导出。
具体操作时不用新建数据库,只需在原来数据库下新建文件组和数据文件即可。方法是在新文件组里重建损坏的表,即新建的表属于新建的文件组,表结构要跟损坏的表完全一致,并将上述原始表数据导入到新表中,最后清空原始表。