SQL Server 简单模式下,误删除堆表记录进行数据恢复(技术贴)

Paddy 2013-01-17 07:16:20
很多朋友认为数据库在简单模式下,堆表误删除一条记录,是无法找回的,因为没有日志记录。其实不然,某种意义上是可以找回的,因为堆表在删除记录时,只更改了行偏移,实际数据没有被物理删除,所以利用这点,测试了下恢复数据,果然成功了,但是还有点问题没有研究出结果:除了更改偏移量,删除数据时还需要更改页眉,这点还没时间去琢磨,所以恢复数据时还要能推断出页眉的16进制对应关系,有兴趣的朋友可以分享下经验给我。

废话不多说,测试的demo如下:

测试环境:

  SQL Server 2008 R2

  数据库:repl_test 简单模式

  测试表:test_del



测试步骤

1.创建测试表test_del,并插入测试数据。


create table test_del( a int identity,b char(10))
go
insert into test_del select 'row 1';
insert into test_del select 'row 2';
insert into test_del select 'row 3';
insert into test_del select 'row 4';
insert into test_del select 'row 5';
go


2.查看测试数据,显示正常。



3.DBCC IND命令来找到数据页id,找到数据页id:219,这个数据页存放了test_del的数据



使用dbcc page查看数据页的内容以及行偏移量

dbcc page(repl_test,1,219,1)
go

输出结果为:

DATA:


Slot 0, Offset 0x60, Length 21, DumpStyle BYTE

Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 21

Memory Dump @0x00000000120CC060

0000000000000000: 10001200 01000000 726f7720 31202020 †........row 1
0000000000000010: 20200200 00†††††††††††††††††††††††††† ...

Slot 1, Offset 0x75, Length 21, DumpStyle BYTE

Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 21

Memory Dump @0x00000000120CC075

0000000000000000: 10001200 02000000 726f7720 32202020 †........row 2
0000000000000010: 20200200 00†††††††††††††††††††††††††† ...

Slot 2, Offset 0x8a, Length 21, DumpStyle BYTE

Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 21

Memory Dump @0x00000000120CC08A

0000000000000000: 10001200 03000000 726f7720 33202020 †........row 3
0000000000000010: 20200200 00†††††††††††††††††††††††††† ...

Slot 3, Offset 0x9f, Length 21, DumpStyle BYTE

Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 21

Memory Dump @0x00000000120CC09F

0000000000000000: 10001200 04000000 726f7720 34202020 †........row 4
0000000000000010: 20200200 00†††††††††††††††††††††††††† ...

Slot 4, Offset 0xb4, Length 21, DumpStyle BYTE

Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 21

Memory Dump @0x00000000120CC0B4

0000000000000000: 10001200 05000000 726f7720 35202020 †........row 5
0000000000000010: 20200200 00†††††††††††††††††††††††††† ...

OFFSET TABLE:

Row - Offset
4 (0x4) - 180 (0xb4)
3 (0x3) - 159 (0x9f)
2 (0x2) - 138 (0x8a)
1 (0x1) - 117 (0x75)
0 (0x0) - 96 (0x60)

其中行偏移量第一行为96 (0x60),实际记录为row 1,row 2: (0x75),row 3: (0x8a),row 4:(0x9f),row 5: (0xb4)



4. 删除第三行数据 a = 3,b = row 3的记录

delete test_del where a = 3
go


说明a=3 b=row3的记录已经被删除。

5.再次查看数据页的行偏移

dbcc page(repl_test,1,219,1)
go

Row - Offset
4 (0x4) - 180 (0xb4)
3 (0x3) - 159 (0x9f)
2 (0x2) - 0 (0x0)
1 (0x1) - 117 (0x75)
0 (0x0) - 96 (0x60)

发现第3行的行偏移量被更改成了0,继续执行

dbcc page(repl_test,1,219,2)
go

DATA:

..

00000000120CC060: 10001200 01000000 726f7720 31202020 †........row 1
00000000120CC070: 20200200 00100012 00020000 00726f77 † ...........row
00000000120CC080: 20322020 20202002 00001000 12000300 † 2 .........
00000000120CC090: 0000726f 77203320 20202020 02000010 †..row 3 ....
00000000120CC0A0: 00120004 00000072 6f772034 20202020 †.......row 4
00000000120CC0B0: 20020000 10001200 05000000 726f7720 † ...........row
00000000120CC0C0: 35202020 20200200 00000021 21212121

发现row3的记录还存在数据页中!

那么猜想,是否将第三行的行偏移量0x0修改回原来的0x8a就可以恢复记录了?

利用winHex工具,打开mdf文件,因为是219页面,8*220 = 1802240字节,所以219的行偏移量应该在1802239处,剩下的工作就很简单了





6.关闭数据库的数据页I/O保护机制,即设置page_verify数据库选项为none,并将repl_test 数据库设置为脱机,利用winhex找到repl_test.mdf文件的1802240结尾处16进制码



alter database repl_test set page_verify none
go
use master
alter database repl_test set offline
go


把repl_test数据库设置为脱机,用winhex工具找到219页面的结尾处(220页面的其实位置):





果然第3行的行偏移量为00 00,那么我将其改回8A 00后保存,(删除记录前,我对该页的页眉16进制代码进行了截图,还原记录时人工对应截图修改回了页眉16进制码)并将数据库设置为online





记录被成功恢复。

可惜小弟不才,还没研究页眉结构对应的物理16进制关系。只靠修改前的页眉截图,修改后按照截图还原页眉,这里无法向大家说明白修改的地方。希望有经验或者有兴趣的朋友可以和我分享下,谢谢~

文笔不好,如果哪里看的模糊请留言。
...全文
889 22 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
WS112030 2015-07-25
  • 打赏
  • 举报
回复
学习了,谢谢!!
Andy-W 2013-01-23
  • 打赏
  • 举报
回复
貌似可行,但重要的一点是如何知道“第三行的行偏移量0x0修改回原来的0x8a”,也就是说删除之后我们如何知道它的来的Offset为0x8a。
rfq 2013-01-22
  • 打赏
  • 举报
回复
删除记录 ,有时 在空间不够的情况 或者有转移记录 的 幻想记录 等这种方法可能不行
szm341 2013-01-18
  • 打赏
  • 举报
回复
引用 15 楼 zc10151 的回复:
引用 11 楼 szm341 的回复:膜拜一下~~那么只是适合堆表吗?还有如果批量删除数据的话。。这个方法可以批量找回吗?聚集索引还没试过,待研究中
支持lz继续研究~早日发布成果给大家分享呵呵
Vidor 2013-01-18
  • 打赏
  • 举报
回复
引用 18 楼 mayuanf 的回复:
不锁表或不删除表的前提下,堆表的delete是不会release page的。详见 http://support.microsoft.com/kb/913399/en-us
不是同一个议题好吧,delete堆表确实不会释放页面为其它对象所用,但不等于自己都不能用。 这里只删除了Slot 2尚不涉及release page,但Slot 2对于该表就是可用空间,随时可以用新纪录填充的,象楼主测试的单页情况下,下一条合适数据就会填充Slot 2。
Paddy 2013-01-18
  • 打赏
  • 举报
回复
引用 12 楼 Vidor 的回复:
首先要知道删除的数据所在的文件号页面号,而且delete之后再多insert几下可能Slot 2就被使用了,所以实际应用上没太大的可操作性。 而且实际的表有N个页面,在Heap的IAM_Chain里面要查出被删除数据页面号,难度可向而知。 知道页面号的情况下,用DBCC WRITEPAGE在线情况下就可以改回来。
槽2可能不是马上就能被使用的,印象中堆表空间释放是在某种情况下才可以,这个文章其实只是个入门级,哈哈~我想继续深究,会找到一个修复数据的可行性方案的~可惜不才啊..
Paddy 2013-01-18
  • 打赏
  • 举报
回复
引用 11 楼 szm341 的回复:
膜拜一下~~那么只是适合堆表吗?还有如果批量删除数据的话。。这个方法可以批量找回吗?
聚集索引还没试过,待研究中
mayuanf 2013-01-18
  • 打赏
  • 举报
回复
引用 16 楼 zc10151 的回复:
引用 12 楼 Vidor 的回复:首先要知道删除的数据所在的文件号页面号,而且delete之后再多insert几下可能Slot 2就被使用了,所以实际应用上没太大的可操作性。 而且实际的表有N个页面,在Heap的IAM_Chain里面要查出被删除数据页面号,难度可向而知。 知道页面号的情况下,用DBCC WRITEPAGE在线情况下就可以改回来。 槽2可能……
不锁表或不删除表的前提下,堆表的delete是不会release page的。详见 http://support.microsoft.com/kb/913399/en-us
發糞塗牆 2013-01-17
  • 打赏
  • 举报
回复
引用 12 楼 Vidor 的回复:
首先要知道删除的数据所在的文件号页面号,而且delete之后再多insert几下可能Slot 2就被使用了,所以实际应用上没太大的可操作性。 而且实际的表有N个页面,在Heap的IAM_Chain里面要查出被删除数据页面号,难度可向而知。 知道页面号的情况下,用DBCC WRITEPAGE在线情况下就可以改回来。
这个我也想过,特别是刚删了又陆陆续续在写入。所以比较好的实践还是对正式环境使用完整模式并创建库后马上做一次完整备份。不过这不是这个文章的主题思想,唠叨几句而已
-Tracy-McGrady- 2013-01-17
  • 打赏
  • 举报
回复
引用 10 楼 DBA_Huangzj 的回复:
引用 9 楼 yangsh0722 的回复:引用 8 楼 DBA_Huangzj 的回复:我是个菜鸟,靠时间累一下分数而已 分数有用?你一边玩泥沙去,没你说话的地方
Vidor 2013-01-17
  • 打赏
  • 举报
回复
首先要知道删除的数据所在的文件号页面号,而且delete之后再多insert几下可能Slot 2就被使用了,所以实际应用上没太大的可操作性。 而且实际的表有N个页面,在Heap的IAM_Chain里面要查出被删除数据页面号,难度可向而知。 知道页面号的情况下,用DBCC WRITEPAGE在线情况下就可以改回来。
szm341 2013-01-17
  • 打赏
  • 举报
回复
膜拜一下~~那么只是适合堆表吗?还有如果批量删除数据的话。。这个方法可以批量找回吗?
發糞塗牆 2013-01-17
  • 打赏
  • 举报
回复
引用 9 楼 yangsh0722 的回复:
引用 8 楼 DBA_Huangzj 的回复:我是个菜鸟,靠时间累一下分数而已 分数有用?
你一边玩泥沙去,没你说话的地方
-Tracy-McGrady- 2013-01-17
  • 打赏
  • 举报
回复
引用 8 楼 DBA_Huangzj 的回复:
我是个菜鸟,靠时间累一下分数而已
分数有用?
發糞塗牆 2013-01-17
  • 打赏
  • 举报
回复
我是个菜鸟,靠时间累一下分数而已
Paddy 2013-01-17
  • 打赏
  • 举报
回复
引用 6 楼 DBA_Huangzj 的回复:
期待后续。另外我听说过能通过mdf找回比较高百分比的数据,估计他们的原理和你的差不多。
斑竹支持,一定努力到底~
發糞塗牆 2013-01-17
  • 打赏
  • 举报
回复
期待后续。另外我听说过能通过mdf找回比较高百分比的数据,估计他们的原理和你的差不多。
Paddy 2013-01-17
  • 打赏
  • 举报
回复
引用 4 楼 x_wy46 的回复:
引用 3 楼 zc10151 的回复:引用 2 楼 x_wy46 的回复:个人感觉这种方式只适合研究数据库的原理,而不适合实际应用 因为在实际情况下,误删数据时不可能提供这么多信息供你做恢复参考的 很多数据是可以自己计算出来的,目前我了解的,除了页眉格式外,其他的都已经能确保推出需要的数据了。 其实可以利用这个原理继续深入, 楼主的探索精神值得学习,真的!……
加油兄弟~ 我也做了管理员几年了,不规范的慢慢规范就好了~这个只是分享下,实际怎么应用还得看怎么去理解了~
专注or全面 2013-01-17
  • 打赏
  • 举报
回复
引用 3 楼 zc10151 的回复:
引用 2 楼 x_wy46 的回复:个人感觉这种方式只适合研究数据库的原理,而不适合实际应用 因为在实际情况下,误删数据时不可能提供这么多信息供你做恢复参考的 很多数据是可以自己计算出来的,目前我了解的,除了页眉格式外,其他的都已经能确保推出需要的数据了。 其实可以利用这个原理继续深入,
楼主的探索精神值得学习,真的!!! 现实的情况是,做的内部系统,很多表主键没有,约束没有,索引没有,到处select * 各种不规范 人们对数据的认识停留在很浅的层次 你写出了一个页面,人家点两下,不报错,再加上整个样式啥的,看起来“还不错” 老板眼里你比那弄数据库的强多了, 弄数据的,老板看不见,摸不着,除了问题还怪你恢复的不及时,没有问题更觉着你没“价值” 哎,数据库不玩到一定的水平,一个数据库管理员的作用看起来微乎其微
Paddy 2013-01-17
  • 打赏
  • 举报
回复
引用 2 楼 x_wy46 的回复:
个人感觉这种方式只适合研究数据库的原理,而不适合实际应用 因为在实际情况下,误删数据时不可能提供这么多信息供你做恢复参考的
很多数据是可以自己计算出来的,目前我了解的,除了页眉格式外,其他的都已经能确保推出需要的数据了。 其实可以利用这个原理继续深入,
加载更多回复(2)

6,128

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server 新技术前沿
社区管理员
  • 新技术前沿社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧