当前位置: 首页 > 科技观察

SQLServer简单模式下堆表误删记录的恢复

时间:2023-03-16 11:59:40 科技观察

很多朋友认为,在简单模式下,如果数据库中的堆表不小心删除了一条记录,因为没有日志记录,所以无法找回。事实上,它不是。从某种意义上说,是可以检索的,因为堆表删除记录时,只是改变了行偏移量,并没有物理删除实际数据。因此,利用这一点,我测试了恢复数据,成功了。但是还有一些问题没有研究清楚:如果不关闭页面校验,删除数据时除了改变偏移量外,还需要改变页眉。这个我还没来得及想,所以恢复数据的时候推断页眉是很有必要的。十六进制的对应关系,有兴趣的朋友可以和我分享经验。这里为了排除header的校验错误,测试关闭后废话不多说。测试demo如下:测试环境:  SQLServer2008R2  数据库:repl_test简单模式  测试表:test_del测试步骤1.创建测试表test_del,插入测试数据。createtabletest_del(aintidentity,bchar(10))去insertintotest_delselect'row1';insertintotest_delselect'row2';insertintotest_delselect'row3';insertintotest_delselect'row4';insertintotest_delselect'row5';3、使用DBCCIND命令查找数据页id,找到数据页id:219,这个数据页存放的是test_del的数据,使用dbccpage查看数据页内容和行偏移dbccpage(repl_test,1,219,1)输出结果为:数据:插槽0,Offset0x60,长度21,DumpStyleByterecordtype=prientar_record记录属性=Null_Bitmap记录尺寸=21memorydump@0x00000000120600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000来。记录尺寸=21memorydump@0x00000000120cc07500000000000000000000000000000000000000000000000000000000000000000000000000000000来????????????...Slot2,Offset0x8a,Length21,DumpStyleBYTERecordType=PRIMARY_RECORDRecordAttributes=NULL_BITMAPRecordSize=21MemoryDump@0x00000000120CC08A0000000000000000:1000120003000000726f772033202020?........第3行0000000000000010:2020020000??????????????????????????...插槽3,偏移量2x9ftype=priendar_record记录属性=null_bitmap记录尺寸=21memorydump@0x00000000120cc09f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000来:100012000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000来†?????????...Slot4,Offset0xb4,Length21,DumpStyleBYTERecordType=PRIMARY_RECORDRecordAttributes=NULL_BITMAPRecordSize=21MemoryDump@0x00000000120CC0B40000000000000000:1000120005000000726f772035202020?........第5行0000000000000010:2020020000?????????????????????...偏移表:行-偏移4(0x4)-0xb180(0xb14)03(0x3)-159(0x9f)2(0x2)-138(0x8a)1(0x1)-117(0x75)0(0x0)-96(0x60)行偏移量***第96行(0x60),实际记录是第1行,第2行:(0x75),第3行:(0x8a),第4行:(0x9f),第5行:(0xb4)4。删除第三行数据a=3,b=row3recorddeletetest_delwherea=3go显示记录a=3b=row3已经被删除5.再次查看数据页的行偏移dbccpage(repl_test,1,219,1)goRow-Offset4(0x4)-180(0xb4)3(0x3)-159(0x9f)2(0x2)-0(0x0)1(0x1)-117(0x75)0(0x0)-96(0x60)发现第三行的行偏移量已经变成了0,继续执行dbccpage(repl_test,1,219,2)goDATA:00000000120CC060:1000120001000000726f772031202020202020202.ROW1000000120CC070:20200200001000120002000000726F77?...................................................................................................................................................................................................................0000726F77203202020202020202002000010?..row3....00000000120CC0A0:00120004000000726F77203420202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020?.......35202020202002000000002121212121发现数据页中还存在row3记录!那么猜想,将第三行的行偏移0x0修改回原来的0x8a,是否可以恢复记录呢?用winHex工具打开mdf文件,因为是219页,8*220=1802240字节,所以219的行偏移应该在1802239,剩下的工作就很简单了6.关闭数据页数据库I/O保护机制,即设置page_verify数据库选项为none,repl_test数据库设置为offline,使用winhex查找repl_test.mdf文件中1802240末尾的十六进制代码alterdatabaserepl_testsetpage_verifynonegousemasteralterdatabaserepl_testsetofflinego将repl_test数据库设置为离线,使用winhex工具找到219页的末尾(220页的实际位置):果然第3行的行偏移量是0000,所以改返回8A00后保存,并设置数据库在线,记录恢复成功。如果不执行alterdatabaserepl_testsetpage_verifynonego,读表时会出现页面校验错误。那么如何安全地检索记录并通过DBCCcheckdb呢?1、笨办法取回记录后删除原表,损坏的页会丢失,重新建表导入数据即可。2.修改header校验,可惜小弟天赋不高,没有研究header结构对应的物理十六进制关系。只靠修改前的表头截图,修改后根据截图还原表头。这里不可能向大家解释修改。希望有经验或者感兴趣的朋友可以分享给我,谢谢~原文链接:http://www.cnblogs.com/SQLServer2012/archive/2013/01/17/2864880.html【编辑推荐】百万数据下几项SQL性能测试熟练使用数据库连接监控组件解决关机问题SQLServer高可用常见问题SQLServer2008R2故障转移集群环境准备SQLServer:局部变量如何影响查询性能