您的位置:澳门新葡8455最新网站 > 数据库管理 > 要事后状态恢复生机,SqlServer索引页损坏苏醒

要事后状态恢复生机,SqlServer索引页损坏苏醒

发布时间:2019-12-31 04:27编辑:数据库管理浏览(54)

     

    问题背景

    运维操作失误,在没有正常关闭sqlserver的情况下,将服务器关闭了,重启后某些表损坏(应该是某些页损坏了,没有损坏的页还能访问到数据,但是访问损坏了的页就有问题),目前数据库只有4.20号的备份。

    错误提示:

    消息 829,级别 21,状态 1,第 1 行
    数据库 ID 15,页 (1:21826) 已标记为 RestorePending,可能表明磁盘已损坏。要从此状态恢复,请执行还原操作。
    

    报错信息

    查询脚本:select * from t_jxjs_pctq where c_bh_tqxx = '8ae480b26320550e016323d098050175';

    报错信息:HY000-[SQL Server] 数据库 ID 11,页[1:60682]已标记为RestorePending,可能表名磁盘已损坏,要从此状态进行恢复,请执行还原操作。

    引起原因:

    RestorePending一般是在进行页恢复的过程中出现的,就是在进行了restore操作之后但还没有进行recovery操作之前页的状态。

    出现这样的问题可以肯定这个表是损坏了,但是在查询数据的时候如果不会查询到损坏页面的数据话是不会报错的,也就是说可以有条件的使用这个表。

    如果损坏的页只有一个的话,那删除掉这个坏表故障肯定就没有了,因为一个页里面只会放一个表的数据。

    损坏的直接原因就是放在磁盘上面的数据被意外的修改了或者写入的时候出错这些,可能是磁盘问题,但是IO系统可能性更大。

    可以好好的检查系统日志和SQLServer的LOG,看看里面有没有关于磁盘或者IO之类的警告、报错信息,以进一步确定原因。

    至于处理方法,如果表重要那就利用备份做页面还原恢复数据,不重要的话就删掉重建,

    或者使用以下方式进行修复,在处理完坏页之后再对整个数据库做一次DBCC CHECKDB操作,确保没有其他的坏页。

    报错可能的原因

    RestorePending一般是在进行页恢复的过程中出现的,就是在进行了restore操作之后但还没有进行recovery操作之前页的状态。出现这样的问题可以肯定这个表是损坏了,但是在查询数据的时候如果不会查询到损坏页面的数据话是不会报错的,也就是说可以有条件的使用这个表。参考资料

    解决办法:

    快速修复
    DBCC CHECKDB ('数据库名', REPAIR_FAST) 
    
    重建索引并修复
    DBCC CHECKDB ('数据库名', REPAIR_REBUILD)
    
    如果必要允许丢失数据修复
    DBCC CHECKDB ('数据库名'', REPAIR_ALLOW_DATA_LOSS)
    

    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.做到未雨绸缪,提前做好备份,每天备份,对备份的数据进行还原测试。做到有”备”无患

     

     

    解决办法:

    此时我们需要将数据库设置成单用户模式:

    右键点击数据库 -> 属性 -> 选项 -> 状态 -> 限制访问 -> 选择Single-> 确定。
    

    错误提示:

    当我们修复完数据库后,需要将其恢复为多用户模式,此时可能出现

    数据库 'xxx' 已打开,并且一次只能有一个用户访问
    

     解决办法:

    在设置多用户模式的时候可能会因为还有其它进程的连接导致设置无法进行,所以需要杀掉所有连接的进程。

    方式一

    USE master;   
    GO   
    DECLARE @SQL VARCHAR(3000);  
    SET @SQL = '';  
    SELECT @SQL = @SQL+'; KILL ' + RTRIM(SPID)  
    FROM [sys].[sysprocesses] AS sps  
    WHERE [sps].[dbid] = DB_ID('test');   
    SET @SQL = SUBSTRING(@SQL, 2, LEN(@SQL));  
    EXEC(@SQL);  
    GO  
    

    方式二

    DECLARE @DBName SYSNAME;  
    SET @DBName = 'BI_Monitor'; --这个是要删除的数据库库名      
    
    DECLARE @KSQL NVARCHAR(1000)  
    DECLARE tb CURSOR LOCAL  
    FOR    
    SELECT  
        KSQL = 'KILL ' + CAST([sps].[spid] AS NVARCHAR(10))  
    FROM [sys].[sysprocesses] AS sps  
    WHERE dbid = DB_ID(@DBName)--查询@DBName相关的线程  
    
    --循环杀掉要删除数据的相关线程  
    OPEN tb  
    FETCH NEXT FROM tb INTO @KSQL  
    WHILE @@FETCH_STATUS = 0    
    BEGIN    
        EXECUTE(@KSQL);  
        FETCH NEXT FROM tb INTO @KSQL  
    END   
    CLOSE tb      
    DEALLOCATE tb  
    

    最后再将相应数据库设置为多用户模式即可。

    ALTER DATABASE [test] SET MULTI_USER;--设置为多用户模式  
    

     

    本文由澳门新葡8455最新网站发布于数据库管理,转载请注明出处:要事后状态恢复生机,SqlServer索引页损坏苏醒

    关键词: