今天终于碰到数据库文件头pfs损坏的案例。
环境:win server 2008+sql 2008 R2 SP2
问题描述:db某些page损坏
问题定位:
1、执行DBCC CHECKDB(DB, 'REPAIR_ALLOW_DATA_LOSS') With No_InfoMsgs, All_ErrorMsgs, TableResults;
Note: 需要关注的是RefFile和RefPage这两个列,而不是File和Pge列。Level,stauts
2 、备份永远是DBA第一条天规
Note: backup也报”SQL Server 检测到基于一致性的逻辑 I/O 错误 pageid 不正确(应为 1:1256,但实际为 0:0)“ ,44码的手瞬间小抖了下,压力指数暴增。
3、
SELECT DB_NAME(database_id),[file_id],page_id, CASE event_type WHEN 1 THEN '823 or 824 or Torn Page' WHEN 2 THEN 'Bad Checksum' WHEN 3 THEN 'Torn Page' WHEN 4 THEN 'Restored' WHEN 5 THEN 'Repaired (DBCC)' WHEN 7 THEN 'Deallocated (DBCC)' END, error_count, last_update_date FROM msdb..suspect_pages
Note: 执行结果10+条记录
PS: SQL 2008 Enterprise及更高版本支持 Mirroring和AlwaysOn 页面自动修复功能.但是仅限于Event ID in (823,824,829)错误。AlwaysOn动态管理视图sys.dm_hadr_auto_page_repair中会记录该自动修复操作
4. DBCC PAGE(dbid,filenum, pagenum,3) WITH TABLERESULTS
Note: 瞬间报一致性错误。观察执行结果: PFS (1:1) 参数对应的value值为 ”NOT ALLOCATED“,可以断定PFS文件头损坏。正常情况下,dbcc page 可以显示详细 KeyHashValue,进而根据sys.partitions定位具体的object
解决方案:
使用REPAIR_ALLOW_DATA_LOSS级别都不能修复data
1、 有全备bak,用restore page 方式:
1) backup log db to disk ='' ---- 备份尾部日志
2) RESTORE DATABASE <database> PAGE='1:57, 1:202, 1:916, 1:1016' FROM disk WITH NORECOVERY; --全备还原损坏的页
3) RESTORE DATABASE <database> from disk='第一步的log备份' ---还原到当前时间点
2. 没有全备,将数据导入到一个新建的空数据库里:
· 通过vs 2010的schema compare, data compare
· SSMS 右键--导入导出数据
· BCP 个人推荐
3. 有全备,但是bak文件也受损
RESTORE DATABASE dbname FROM DISK='‘ WITH CONTINUE_AFTER_ERROR
补充:
DBCC CHECK在REPAIR_ALLOW_DATA_LOSS过程中发现页内数据有问题,是把整个页面的数据删除,以维护数据的一致性,所以会丢数据。
DBCC CHECK需要多长时间跟DB的大小,io错误量有关。进度查询可以查看sys.dm_exec_requests中percent_complete字段信息
2014-4-30补充:
什么是管理区分配页?
管理区分配页是数据文件中特殊的页,用来跟踪和管理区分配,本篇将关注三种:
全局分配映射表 (GAM):记录已分配的区,对于一个数据文件,每4GB会有一个GAM页,它的ID总是为2,之后每511,232页出现一次。
Page ID = 2 or Page ID % 511232
共享全局分配映射表 (SGAM) :记录当前用作混合区且至少有一个未使用的页的区,每4GB会有一个SGAM页,它的ID总是为3,之后每511,232页出现一次。
Page ID = 3 or (Page ID – 1) % 511232
页可用空间 (PFS):记录每页的分配状态,是否已分配单个页以及每页的可用空间量,每64MB会有一个PFS页,它的ID总是为1,之后每8,088页出现一次。
Page ID = 1 or Page ID % 8088
如果PAGE id是1/2/3,那么很明显会知道他们是什么管理区分配页,如果PAGE ID很大,那么我们有两个办法来区分它们:
一个办法是采用SQL脚本计算:
Declare @PageID int; -- Enter page number -- e.g., 8088 = PFS page Set @PageID = 8088; Select Case When @PageID = 1 Or @PageID % 8088 = 0 Then 'Is PFS Page' When @PageID = 2 Or @PageID % 511232 = 0 Then 'Is GAM Page' When @PageID = 3 Or (@PageID - 1) % 511232 = 0 Then 'Is SGAM Page' Else 'Is Not PFS, GAM, or SGAM page' End
另一个办法是采用DBCC PAGE来看它的m_type值: