SQL Server 损坏修复 之三 不同部位损坏的应对

2024-01-15 15:48

本文主要是介绍SQL Server 损坏修复 之三 不同部位损坏的应对,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

如果数据库或数据库备份受损,在检查数据库完整性的时候,会发现各种各样的错误。在这一节里,我们针对不同的损坏部位,给出不同的应对方法。首先,我们创建一个测试数据库。

 

CREATE DATABASE TESTDB

GO

USE TESTDB

GO

CREATE TABLE TESTTABLE

(ID int,

NAME nvarchar(50)

)

 

 --建立两个索引,其中一个是聚集索引,另外一个是非聚集索引

CREATE CLUSTERED INDEX idx1 on TESTTABLE (ID)

CREATE INDEX idx2 on TESTTABLE(NAME)

 

 -- 插入300行数据

DECLARE @i INT

SET @i = 1

WHILE (@i <= 300)

BEGIN

INSERT INTO TESTTABLE VALUES(@i, CONCAT('name_', @i))

SET @i = @i + 1

END

 

备份文件损坏

 

对前面建立的测试数据库做一个全备份,名为TESTDB.BAK。因为数据库比较小,所以备份文件也会比较小,用二进制文件编辑器如UltraEdit更改此文件一些地方的内容,以模拟该文件受损的情形,另存为TESTDB_BAD.BAK。执行如下命令,对数据库进行恢复:

RESTORE DATABASE TESTDB FROM DISK='D:\temp\TESTDB_BAD.bak'

 

在做恢复的时候,可能会碰到如下的错误,并且恢复过程会中断。

Processed 312 pages for database 'TESTDB', file 'TESTDB' on file 1.

Processed 3 pages for database 'TESTDB', file 'TESTDB_log' on file1.

Msg 3167, Level 16, State 1, Line 1

RESTORE could not start database 'TESTDB'.

Msg 3013, Level 16, State 1, Line 1

RESTORE DATABASE is terminating abnormally.

Msg 824, Level 24, State 2, Line 1

SQL Server detected a logical consistency-based I/O error: incorrectchecksum (expected: 0x9cb7ecfe; actual: 0x6f316d79). It occurred during a readof page (1:19) in database ID 8 at offset 0x00000000026000 in file 'C:\ProgramFiles\Microsoft SQL Server\MSSQL11.DENALI\MSSQL\DATA\TESTDB.mdf'.  Additional messages in the SQL Server errorlog or system event log may provide more detail. This is a severe errorcondition that threatens database integrity and must be corrected immediately.Complete a full database consistency check (DBCC CHECKDB). This error can becaused by many factors; for more information, see SQL Server Books Online.

 

这时,可以使用WITH CONTINUE_AFTER_ERROR参数尝试再次恢复:

RESTORE DATABASE TESTDB FROM DISK='D:\temp\TESTDB_BAD.bak'

 WITH CONTINUE_AFTER_ERROR

 

加CONTINUE_AFTER_ERROR, 虽然备份文件受损,但是会尽最大可能来继续恢复,显示恢复完成。

Processed 312 pages for database 'TESTDB', file 'TESTDB' on file 1.

Processed 3 pages for database 'TESTDB', file 'TESTDB_log' on file1.

Restore was successful but deferred transactions remain. Thesetransactions cannot be resolved because there are data that is unavailable.Either use RESTORE to make that data available or drop the filegroups if younever need this data again. Dropping the filegroup results in a defunctfilegroup.

RESTORE WITH CONTINUE_AFTER_ERROR was successful but some damage wasencountered. Inconsistencies in the database are possible.

RESTORE DATABASE successfully processed 315 pages in 0.271 seconds(9.055 MB/sec).

 

不幸的是,对数据库做完恢复后,该数据库有可能会马上处于Suspect(质疑)状态。我们还是无法使用该数据库。 我们把该数据库设置为紧急状态,并执行数据库修复命令:

ALTER DATABASE TESTDB SET EMERGENCY

DBCC CHECKDB(TESTDB)

DBCC CHECKDB(TESTDB, REPAIR_ALLOW_DATA_LOSS)

use TESTDB

SELECT * FROM TESTTABLE

 

上述命令都无法执行成功,并且会报如下错误:

Msg 945, Level 14, State 2, Line 4

Database 'TESTDB' cannot be opened due to inaccessible files orinsufficient memory or disk space.  Seethe SQL Server errorlog for details.

 

碰到这种情况,即使在恢复数据库的时候使用CONTINUE_AFTER_ERROR参数,但是还是无济于事。对于这个受损的备份文件,我们只能遗弃。

 

根据备份文件受损的地方不同,有时候还是能够使用上述方法找回一部分数据的。这很大部分依赖于在备份文件中受损的位置。所以CONTINUE_AFTER_ERROR这个参数,对于受损的数据库备份恢复,虽然是一个不错的尝试,但也并不是万能的。

 

日志文件损坏

下面是一个模拟日志文件损坏的测试。请运行如下的脚本操作。第一个UPDATE语句是更新ID=295的数据,并且马上做一个CHECKPOINT的动作,这会使得数据的更新,立刻写入数据文件中。第二个UPDATE语句打算要更新ID=296的数据,这两个操作是放在同一个事务内进行的。但是在第一个UPDATE语句结束后,不等WAITFOR语句结束,请马上杀掉数据库服务进程(Sqlservr.exe)。这样,在数据文件里,ID=295的记录已经被修改,ID=296的记录还没被修改。如果日志文件不损坏,下次SQLServer重新启动时,SQL会发现这个做到一半的事物,将ID=295的记录修改回滚。

BEGIN TRANSACTION

UPDATE TESTTABLE SET NAME = 'xxxx' where ID = 295

CHECKPOINT

WAITFOR DELAY '0:1:0'

UPDATE TESTTABLE SET NAME = 'xxxx' where ID = 296

COMMIT TRANSACTION

 

在这个测试里,我们编辑该数据库的日志文件,人为对数据库的日志文件造成损坏。当该数据库服务重新启动的时候,该数据库会处于RECOVERY_PENDING状态,查看数据库错误日志,有如下的错误,表明日志文件受损。

2012-04-15 22:49:57.930    spid26s       Error: 824, Severity: 24, State: 2.

2012-04-15 22:49:57.930    spid26s       SQL Server detected a logicalconsistency-based I/O error: incorrect checksum (expected: 0x6c1e7b9c; actual:0xd6a87d4a). It occurred during a read of page (2:0) in database ID 8 at offset0000000000000000 in file 'C:\Program Files\Microsoft SQL Server\MSSQL11

2012-04-15 22:49:57.930    spid26s       Error: 5105, Severity: 16, State: 1.

2012-04-15 22:49:57.930    spid26s       A file activation error occurred. Thephysical file name 'C:\Program Files\Microsoft SQLServer\MSSQL11.DENALI\MSSQL\DATA\TESTDB_log.ldf' may be incorrect. Diagnose andcorrect additional errors, and retry the operation.

2012-04-15 22:49:57.950    spid26s       File activation failure. The physicalfile name "C:\Program Files\Microsoft SQLServer\MSSQL11.DENALI\MSSQL\DATA\TESTDB_log.ldf" may be incorrect.

2012-04-15 22:49:57.950    spid26s       The log cannot be rebuilt because therewere open transactions/users when the database was shutdown, no checkpointoccurred to the database, or the database was read-only. This error could occurif the transaction log file was manually deleted or lost due to a

 

针对这种日志损坏,读者可以用下面的方法,重建数据库日志,以恢复数据库的正常访问:

ALTER DATABASE TESTDB SET EMERGENCY

ALTER DATABASE TESTDB Rebuild LOG on

(name=xxxx_log, filename='D:\temp\xxxx_log.LDF')

ALTER DATABASE TESTDB SET MULTI_USER

 

在重建数据库日志以后,一定要运行DBCC CHECKDB以确保没有一致性错误。

 

有一个要注意的地方,对数据库日志进行重建以后,我们会发现ID为295的NAME变成了xxxx,而ID为296的NAME依旧是name_296。如下图10-2所示。这从数据库的逻辑上来讲是没有问题的。但是从应用的角度,可能会大有问题。应用程序可能有逻辑需求,要求ID为295和ID为296的名字要么同时改变,要么同时不改变,在应用程序中,我们开启了事务以确保这一点。但是经过日志重建后,这点不能得到保证了。

10- 2 重建数据库日志后,保存下来的数据

因此,重建数据库的日志文件,是迫不得已的办法,会破坏数据的一致性。还是建议从好的数据库备份中恢复数据,这样能保证数据在业务逻辑上的一致。

 

用户数据文件损坏

让我们接着模拟数据库文件损坏的各种情形。新建测试数据库后,把TESTDB下线,编辑该数据库的数据文件,以模拟该数据文件受损的情形。然后把该数据库上线,运行DBCC CHECKDB命令,会报告发现数据库受损。根据受损的部位,有以下几个情形:

·        非聚集索引页面受损

具体的错误信息如下:

Msg 8939, Level 16, State 98, Line 1

Table error: Object ID 245575913, index ID 2, partition ID72057594039173120, alloc unit ID 72057594043564032 (type In-row data), page(1:228). Test (IS_OFF (BUF_IOERR, pBUF->bstat)) failed. Values are 133129and -4.

Msg 8928, Level 16, State 1, Line 1

Object ID 245575913, index ID 2, partition ID 72057594039173120,alloc unit ID 72057594043564032 (type In-row data): Page (1:228) could not beprocessed.  See other errors for details.

Msg 8980, Level 16, State 1, Line 1

Table error: Object ID 245575913, index ID 2, partition ID72057594039173120, alloc unit ID 72057594043564032 (type In-row data). Indexnode page (1:264), slot 0 refers to child page (1:228) and previous child(0:0), but they were not encountered.

Msg 8978, Level 16, State 1, Line 1

Table error: Object ID 245575913, index ID 2, partition ID72057594039173120, alloc unit ID 72057594043564032 (type In-row data). Page(1:265) is missing a reference from previous page (1:228). Possible chainlinkage problem.

DBCC results for 'TESTTABLE'.

There are 300 rows in 2 pages for object "TESTTABLE".

CHECKDB found 0 allocation errors and 4 consistency errors in table'TESTTABLE' (object ID 245575913).

 

读者会发现数据库受损是发生在表格TESTTABLE,而且是索引编号为2的存储上面。索引编号大于1的都是非聚集索引。因此,读者可以重建非聚集索引来尝试对数据库进行修复。

 

DROP INDEX idx2 ON TESTTABLE

GO

CREATE INDEX idx2 ON TESTTABLE(NAME)

 

再次运行DBCC CHECKDB(TESTDB)命令,检查结果显示,数据库中的数据是一致的。由于受损的地方恰好在非聚集索引页上,所以我们对数据库可以进行不丢失数据修复,如果受损在其他地方,如聚集索引页,则丢失数据难以避免。

·        聚集索引页面受损

 

根据第八章数据库空间管理中提到的方法,我们执行下面一系列查询,然后对聚集索引页面进行篡改:

 

SELECT * FROM sys.objects WHERE NAME = 'TESTTABLE'

-- 结果为245575913

 

SELECT * FROM sys.partitions WHERE object_id = '245575913'

-- 有两个索引,其中聚集索引所在地partition_id为72057594039107584

 

SELECT * FROM sys.system_internals_allocation_units WHERE

container_id = '72057594039107584'

-- 得知第一个数据页为(1, 226)

 

DBCC TRACEON(3604)

DBCC PAGE('TESTDB', 1, 226, 1)

DBCC TRACEOFF(3604)

-- 得知数据页的具体内容,编辑数据文件,找到相应位置,对该数据页面的内容进行篡改

 

得知数据页的具体内容后,把该数据库下线, 根据第一个数据页的查询得到的字符串,编辑数据文件,对该数据页内容进行篡改。然后上线该数据库,并对数据库做一致性检查,会发现如下错误:

Msg 8978, Level 16, State 1, Line 1

Table error: Object ID 245575913, index ID 1, partition ID72057594039107584, alloc unit ID 72057594043498496 (type In-row data). Page(1:231) is missing a reference from previous page (1:226). Possible chainlinkage problem.

Msg 8939, Level 16, State 98, Line 1

Table error: Object ID 245575913, index ID 1, partition ID72057594039107584, alloc unit ID 72057594043498496 (type In-row data), page(1:226). Test (IS_OFF (BUF_IOERR, pBUF->bstat)) failed. Values are 133129and -4.

Msg 8928, Level 16, State 1, Line 1

Object ID 245575913, index ID 1, partition ID 72057594039107584,alloc unit ID 72057594043498496 (type In-row data): Page (1:226) could not beprocessed.  See other errors for details.

Msg 8980, Level 16, State 1, Line 1

Table error: Object ID 245575913, index ID 1, partition ID72057594039107584, alloc unit ID 72057594043498496 (type In-row data). Indexnode page (1:230), slot 0 refers to child page (1:226) and previous child(0:0), but they were not encountered.

DBCC results for 'TESTTABLE'.

There are 63 rows in 1 pages for object "TESTTABLE".

CHECKDB found 0 allocation errors and 4 consistency errors in table'TESTTABLE' (object ID 245575913).

 

这次,数据页受损是在聚集索引页面上(索引编号为1),也就是在数据页面上,因此我们无法通过重建索引的方法对数据进行修复。我们采用REPAIR_ALLOW_DATA_LOSS的方法,对数据库进行修复。

 

ALTER DATABASE TESTDB set EMGERGENCY

ALTER DATABASE TESTDB set single_user withrollback immediate

GO

DBCC CHECKDB(TESTDB, 'REPAIR_ALLOW_DATA_LOSS')

GO

ALTER DATABASE TESTDB set multi_user

 

检索修复后的数据库,读者会发现,原来有300条记录的,现在只剩下了63条。有237条数据在修复的过程中丢失了。这是因为修复程序发现页内数据有问题,采用比较保守的办法,是把整个页面的数据删除,以维护数据的一致性。

 

·        系统页面受损

 

数据库有一些系统页面或系统表格非常重要,如在第八章数据库空间管理提及的PFS页面,GAM页面和SGAM页面。如果这些页面受损,那么整个数据库基本上很难修复。下面是一个示例。首先查询TESTDB的GAM页面信息,得到Slot 1里面的数据0000381f。

 

 

把数据库TESTDB下线,用二进制文件编辑器打开数据文件,对字符串0000381f进行篡改,然后把该数据库上线。数据库一上线后,TESTDB马上处于置疑(Suspect)状态,如图10-5所示。

 

我们把该数据库设为紧急模式,然后执行DBCC CHECKDB(TESTDB)

 

ALTER DATABASE TESTDB SET Emergency

DBCC CHECKDB(TESTDB)

 

会报如下的错误信息:

DBCC results for 'TESTDB'.

Msg 8998, Level 16, State 1, Line 1

Page errors on the GAM, SGAM, or PFS pages prevent allocationintegrity checks in database ID 5 pages from (1:0) to (1:517631). See othererrors for cause.

 

用下面的命令尝试对数据库进行修复:

ALTER DATABASE TESTDB SET Emergency

ALTER DATABASE TESTDB set single_user with rollback immediate

DBCC CHECKDB(TESTDB, 'REPAIR_ALLOW_DATA_LOSS')

 

由于系统页面损坏,所以即使用REPAIR_ALLOW_DATA_LOSS也无法对数据库进行修复。修复程序提示如下错误:

Msg 5028, Level 16, State 4, Line 3

The system could not activate enough of the database to rebuild thelog.

DBCC results for 'TESTDB'.

CHECKDB found 0 allocation errors and 0 consistency errors indatabase 'TESTDB'.

Msg 7909, Level 20, State 1, Line 3

The emergency-mode repair failed.You must restore from backup.

 

因此,如果系统页面或者系统表受损,则只能从备份当中恢复数据。

 

10.3.4 系统数据库损坏

 

系统数据库包括master数据库,tempdb数据库,MSDB数据库和MODEL数据库。由于tempdb数据库会在数据库重新启动时,再次创建,因此对于tempdb数据库的损坏,我们可以重新启动数据库对其进行恢复。

 

master数据库,MSDB数据库和MODEL数据库一般情况下不会进行很多改动,所以如果碰到系统数据库损坏,从备份中恢复系统数据库是最好的办法。如果我们没有对系统数据库做备份,那么修复系统数据库的方法,则是从相同版本的数据库上,拷贝系统数据库的数据文件和日志文件,然后替换受损的系统数据库。具体方法,可参考前面章节对移动数据库的介绍。这样,损失的数据,包括master里面的数据和MSDB里面的数据。用户可以通过重建登录以及重建Job来进行恢复。

这篇关于SQL Server 损坏修复 之三 不同部位损坏的应对的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/609413

相关文章

SQL中的外键约束

外键约束用于表示两张表中的指标连接关系。外键约束的作用主要有以下三点: 1.确保子表中的某个字段(外键)只能引用父表中的有效记录2.主表中的列被删除时,子表中的关联列也会被删除3.主表中的列更新时,子表中的关联元素也会被更新 子表中的元素指向主表 以下是一个外键约束的实例展示

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

如何去写一手好SQL

MySQL性能 最大数据量 抛开数据量和并发数,谈性能都是耍流氓。MySQL没有限制单表最大记录数,它取决于操作系统对文件大小的限制。 《阿里巴巴Java开发手册》提出单表行数超过500万行或者单表容量超过2GB,才推荐分库分表。性能由综合因素决定,抛开业务复杂度,影响程度依次是硬件配置、MySQL配置、数据表设计、索引优化。500万这个值仅供参考,并非铁律。 博主曾经操作过超过4亿行数据

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

2. c#从不同cs的文件调用函数

1.文件目录如下: 2. Program.cs文件的主函数如下 using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using System.Windows.Forms;namespace datasAnalysis{internal static

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

MySQL高性能优化规范

前言:      笔者最近上班途中突然想丰富下自己的数据库优化技能。于是在查阅了多篇文章后,总结出了这篇! 数据库命令规范 所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用mysql保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来) 数据库对象的命名要能做到见名识意,并且最后不要超过32个字符 临时库表必须以tmp_为前缀并以日期为后缀,备份

uva 10061 How many zero's and how many digits ?(不同进制阶乘末尾几个0)+poj 1401

题意是求在base进制下的 n!的结果有几位数,末尾有几个0。 想起刚开始的时候做的一道10进制下的n阶乘末尾有几个零,以及之前有做过的一道n阶乘的位数。 当时都是在10进制下的。 10进制下的做法是: 1. n阶位数:直接 lg(n!)就是得数的位数。 2. n阶末尾0的个数:由于2 * 5 将会在得数中以0的形式存在,所以计算2或者计算5,由于因子中出现5必然出现2,所以直接一

[MySQL表的增删改查-进阶]

🌈个人主页:努力学编程’ ⛅个人推荐: c语言从初阶到进阶 JavaEE详解 数据结构 ⚡学好数据结构,刷题刻不容缓:点击一起刷题 🌙心灵鸡汤:总有人要赢,为什么不能是我呢 💻💻💻数据库约束 🔭🔭🔭约束类型 not null: 指示某列不能存储 NULL 值unique: 保证某列的每行必须有唯一的值default: 规定没有给列赋值时的默认值.primary key: