SqlServer索引页损坏恢复
问题背景
运维操作失误,在没有正常关闭sqlserver的情况下,将服务器关闭了,重启后某些表损坏(应该是某些页损坏了,没有损坏的页还能访问到数据,但是访问损坏了的页就有问题),目前数据库只有4.20号的备份。
报错信息
查询脚本:select * from t_jxjs_pctq where c_bh_tqxx = '8ae480b26320550e016323d098050175';
报错信息:hy000-[sql server] 数据库 id 11,页[1:60682]已标记为restorepending,可能表名磁盘已损坏,要从此状态进行恢复,请执行还原操作。
报错可能的原因
restorepending一般是在进行页恢复的过程中出现的,就是在进行了restore操作之后但还没有进行recovery操作之前页的状态。出现这样的问题可以肯定这个表是损坏了,但是在查询数据的时候如果不会查询到损坏页面的数据话是不会报错的,也就是说可以有条件的使用这个表。
5.7号和4.20号的数据量对比
表名 | 4.20号 | 5.6号 |
---|---|---|
t_jxjs_pctq | 1716 | 2175 |
t_ywgy_wsqd_ws | 7358 | 8275 |
t_jxjs_hyjl | 244 | 287 |
数据库修复
--修复改数据库 1.此时我们需要将数据库设置成单用户模式: 右键点击数据库 -> 属性 -> 选项 -> 状态 -> 限制访问 -> 选择single-> 确定。注意修复完成后需要改回多用户模式。 --2.使用dbcc checkdb进行数据库修复 dbcc checkdb ('db_xfzx', repair_fast) --修复过程中报错信息: t_jxjs_hyjl的 dbcc 结果。 消息 8928,级别 16,状态 2,第 1 行 对象 id 885578193,索引 id 1,分区 id 72057594060341248,分配单元 id 72057594075873280 (类型为 in-row data): 无法处理页 (1:70890)。有关详细信息,请参阅其他错误消息。 dbcc 语句的修复级别导致避开了此修复。 消息 8939,级别 16,状态 98,第 1 行 表错误: 对象 id 885578193,索引 id 1,分区 id 72057594060341248,分配单元 id 72057594075873280 (类型为 in-row data),页 (1:70890)。测试(is_off (buf_ioerr, pbuf->bstat))失败。值为 12584969 和 -6。 修复此错误要求首先修正其他错误。 消息 8976,级别 16,状态 1,第 1 行 表错误: 对象 id 885578193,索引 id 1,分区 id 72057594060341248,分配单元 id 72057594075873280 (类型为 in-row data)。在扫描过程中未发现页 (1:70890),但该页的父级 (1:704) 和上一页 (1:450709) 都引用了它。请检查以前的错误消息。 修复此错误要求首先修正其他错误。 对象 't_jxjs_hyjl' 的 6 页中有 249 行。 checkdb 在表 't_jxjs_hyjl' (对象 id 885578193)中发现 0 个分配错误和 3 个一致性错误。 --3.重建索引并修复,报一样的错 dbcc checkdb ('db_xfzx', repair_rebuild) --4.在修复过程中发现t_ywgy_wsqd_ws,t_jxjs_hyjl均有此报错。同时检查其他库没有发现有损坏情况。 --5.尝试进行单个表修复,以及对损坏页的单独修复,均会报上面的的错。 dbcc checktable('t_jxjs_pctq',repair_rebuild) dbcc page(11,1,60682,3)
dbcc checkdb并未能解决问题。
重建索引
1.执行了dbcc checkdb后,报错的信息里有索引 id 1;这个信息的提供,可能是索引页的损坏。但是前面执行的dbcc checkdb ('db_xfzx', repair_rebuild)重建索引修复,并没能解决问题。
2.猜测:因为一个表中有多个索引,所以是不是单独重新生成每一个索引就能发现是哪个索引有问题呢?
3.在sqlserver客户端工具上面,对表t_jxjs_hyjl包括主键在内的三个索引进行重新生成,过程中有一个普通索引(i_jxjs_pctq_tqxx)的重新生成失败了,报错信息和最开始查询的信息一样。尝试重新组织该索引还是一样的问题。那么问题就出在i_jxjs_pctq_tqxx这个普通索引上了。
4.既然重建索引失败了,尝试删除该索引,发现可以删除,再重新创建该索引。
5.重建完成后再修复,dbcc checkdb ('db_xfzx', repair_fast) 。这时异常信息里面没有t_jxjs_hyjl表的异常信息。查看表中的数据已经正常,异常的数据可以正常查询,数据量的统计也已经正常。
6.同样t_ywgy_wsqd_ws该表有一个普通索引重新生成有问题,采用上面的方法也能解决。而t_jxjs_hyjl这张表的数据出现重建异常的是主键,由于有主键约束,所以不能删除索引,尝试修改为非主键,但是报错和查询一样的的错误。看来主键的数据不能这么做。最终由于该表只有两百多条数据,而且并不重要,直接恢复了4.20号的数据。
7.当然对表t_ywgy_wsqd_ws也可以采用将该表的数据通过select * into tablea from tableb;的形式插入到另外的表,重新创建该表后将数据恢复回来,然后重建索引。
结语
1.运行dbcc checkdb(db_name)检查数据库的完整性。根据日志判断可能由于某个索引的索引页缺失,索引不完整,导致某些数据查询的异常。而重新生成索引,不能成功,可以先删除该索引,再重新创建。
2.如果是主键索引则可以采用数据迁移的方式。
3.需要注意的是修复过程中不要使用dbcc checkdb ('数据名'', repair_allow_data_loss),repair_allow_data_loss该语句是可能丢失数据的。
4.修复完成后需要从单用户模式修改为多用户模式。
5.做到未雨绸缪,提前做好备份,每天备份,对备份的数据进行还原测试。做到有”备”无患