关于鬼影记录的翻译一
关于鬼影记录的翻译一
原文链接:http://www.sqlskills.com/blogs/paul/inside-the-storage-engine-ghost-cleanup-in-depth/
很多年前,我在各种各样的论坛看到关于鬼影清理任务的帖子当我还在存储引擎开发团队的时候。
之前鬼影清除任务有一些bug在里面,KB文章是KB932115和KB815594,不过这两篇文章介绍鬼影机制的信息还是很少
由于某些原因,在我的旧的博客里面没有提及鬼影,今天我打算写一篇深入鬼影清理任务的文章
那么,什么是鬼影清理呢?这是一个后台进程用来清理鬼影记录的.通常与鬼影清理任务一起被提及.
什么是鬼影记录呢?上星期我曾经在anatomy OF a record post主题里简短描述了一下,在一张有索引的表里已经
删除了的一条记录或者在快照隔离级别的数据库里,一张没有任何索引的表里已经删除了的一条记录那条记录就是鬼影记录
从聚集索引表这方面讲述鬼影记录会好一些,鬼影就是一个delete操作,但是他实际不是从数据页进行物理删除,他只是
在数据页标记那条记录“已经删除了”或“已经鬼影了”。这对于删除操作的快速执行是一个性能的优化。
这也让删除操作的回滚执行得更快因为只需要把“已经删除了的”或“已经鬼影了的”的记录重新标记为“未删除“,这样就不用
重新插入已经删除了的记录。已经删除了的记录将会在稍后被一个后台的鬼影清理进程/任务物理删除(删除了的记录的那个位置,
不会被其他记录重写)。鬼影记录会在空的数据页面或者空的索引页面插入一条记录,以避免那个空的数据页面或者空的索引页面
被释放,当回滚事务的时候能保证那个数据页或索引页还存在
鬼影记录清理任务不能物理删除已经被标记为鬼影记录的的记录,除非你提交你的事务,确认删除操作已经提交,因为已经删除的记录
有锁,直到提交事务的时候才会解锁。顺便说一下,如果一条记录被标记为“已经删除了的”在一个数据页上,那么,就算加上nolock选项
或者使用READ UNCOMMITTED隔离级别也不能读取得到已经标记为“鬼影”的那条记录
当一条记录已经被删除,并且标记为鬼影记录,那么鬼影记录所在的那个数据页也会被标记为鬼影页面,在PFS页面会有所体现
并且信息会记录在数据页的页面头部。在PFS页记录当前存在鬼影记录的数据页,也能提醒数据库,当前数据库中存在一些鬼影记录
还没有去清理。鬼影记录清理任务不知道哪些数据页面有鬼影记录就算有删除操作发生。只有当扫描操作读取到已经标记为鬼影的
数据页面的时候。
鬼影记录清理任务每5秒钟进行一次查找鬼影记录的任务。扫描任务通过每个数据库的PFS页,查看是否有鬼影记录页面存在,
如果存在就通知鬼影清理任务清理鬼影记录页面里的鬼影记录。如果不存在,扫描任务就去扫描下一个数据库。
鬼影清理任务每次清理最多10个页面的鬼影记录,在我印象中好像是10个页面,以确保系统不会有任何影响。
所以,鬼影记录最终一定会被删除,通过鬼影清理任务。如果鬼影清理任务扫描到当前数据库没有鬼影记录,那么他就会
跳过这个数据库去扫描下一个数据库
您能够告诉鬼影记录清理任务是怎样工作的吗?在SQLSERVER2005里,您能够使用下面的代码在
sys.dm_exec_requests视图里查看鬼影清理任务
1 USE [tempdb] 2 GO 3 SELECT * 4 INTO ##myexecrequests 5 FROM sys.dm_exec_requests 6 WHERE 1 = 0; --where 1=0 只复制架构 7 GO 8 SET NOCOUNT ON; 9 GO 10 DECLARE @a INT 11 SELECT @a = 0; 12 WHILE ( @a < 1 ) 13 BEGIN 14 INSERT INTO ##myexecrequests 15 SELECT * 16 FROM sys.dm_exec_requests 17 WHERE command LIKE '%ghost%' 18 SELECT @a = COUNT(*) 19 FROM ##myexecrequests 20 END; 21 GO 22 SELECT * 23 FROM ##myexecrequests; 24 GO 25 DROP TABLE [##myexecrequests]
如果是SQLSERVER2000 你需要使用 sysprocesses ,SQL2005也可以使用sysprocesses ,不过他也是继承自DMV
1 SELECT * 2 INTO mysysprocesses 3 FROM master.dbo.sysprocesses 4 WHERE 1 = 0; 5 GO 6 SET NOCOUNT ON; 7 GO 8 DECLARE @a INT 9 SELECT @a = 0; 10 WHILE ( @a < 1 ) 11 BEGIN 12 INSERT INTO mysysprocesses 13 SELECT * 14 FROM master.dbo.sysprocesses 15 WHERE cmd LIKE '%ghost%' 16 SELECT @a = COUNT(*) 17 FROM mysysprocesses 18 END; 19 GO 20 SELECT * 21 FROM mysysprocesses; 22 GO
1 在 sys.dm_exec_requests 输出的结果像下面这样 不过有很多列不需要用到的,需要去除掉 2 3 session_id request_id start_time status command 4 ———- ———– ———————– ———— —————- 5 15 0 2007-10-05 16:34:49.653 background GHOST CLEANUP
-------------------------------------------------华丽的分割线-------------------------------------------------
那么,你告诉我,一条记录怎样变为ghost 记录的?
让我们让DBCC PAGE命令看一下数据页面里的内容吧
我会删除一些不是很重要的的位和高亮重要的鬼影记录部分
做下面实验之前确保您的数据库是 完整恢复模式 ,不能是简单恢复模式,所以不要选择tempdb来做实验
不然第八个步骤看不到PFS页的修改
先执行下面的sql语句
1 USE [GPOSDB] 2 GO 3 CREATE TABLE t1 ( c1 CHAR(10) ) 4 CREATE CLUSTERED INDEX t1c1 ON t1 (c1) 5 GO 6 BEGIN TRAN PaulsTran 7 INSERT INTO t1 VALUES ( 'PAUL' ) 8 INSERT INTO t1 VALUES ( 'KIMBERLY' ) 9 DELETE FROM t1 WHERE c1 = 'KIMBERLY'; 10 GO 11 12 DBCC IND ('GPOSDB', 't1', 1); 13 GO 14 ----------------------- 15 --补充一下:DBCC IND 里的page type列 16 PageType 17 Page type: 18 1 = data page, 19 2 = index page, 20 3 = LOB_MIXED_PAGE, 21 4 = LOB_TREE_PAGE, 22 10 = IAM page 23 24 ---------------------- 25 DBCC TRACEON (3604); 26 GO 27 DBCC PAGE ('gposdb', 1, 482, 3); 28 DBCC PAGE ('gposdb', 1, 482, 2);
让我们看一下在在删除动作的过程当中事务日志里发生了什么(请记住在测试数据库上做实验,不然后果自负)
我已经去除了大部分没有用的列
1 DECLARE @a CHAR(20) 2 SELECT @a = [Transaction ID] FROM fn_dblog(NULL, NULL) 3 WHERE [Transaction Name] = 'PaulsTran' 4 5 SELECT * FROM fn_dblog(NULL, NULL) 6 WHERE [Transaction ID] = @a; 7 GO
可以看到有两个插入然后跟着一个删除,删除记录标记为了鬼影记录
不过,在哪里更新PFS页呢?更新PFS页里的鬼影位不是一个事务的一部分。我们需要用另一方法查找,
除非在事务日志记录里dump所有的步骤并动手查找
1 SELECT Description, * FROM fn_dblog (null, null) 2 WHERE Context like '%PFS%' AND AllocUnitName like '%t1%'; 3 GO
首先PFS分配了一页,然后第二个就是我们要找的,他为了告诉大家有鬼影记录就改变了位图里面的位。
下面提交事务,看一下有什么事发生,过滤掉先前的事务日志
1 --先执行下面这句,看一下最大的LSN的LSN号是多少 2 SELECT MAX ([Current LSN]) FROM fn_dblog (null, null); 3 GO 4 – 00000014:000000fc:020c 5 ----------------------------------------------------- 6 --然后提交事务 7 COMMIT TRAN 8 GO 9 SELECT [Page ID], * FROM fn_dblog (null, null) WHERE [Current LSN] > '00000014:000000fc:020c'; --填入最大的LSN号
我们看到几乎是只要提交事务,鬼影记录清理任务就会进行清理工作。
PFS页也进行了位图中的位的相关改变,因为鬼影记录已经没有了,所以他也要改变位图中相应的位
现在检查一下页面dump信息确保记录已经删除掉了,还有页面的头部信息不再显示鬼影记录了
1 DBCC PAGE ('gposdb', 1, 324, 3); 2 GO 3 4 DBCC PAGE ('gposdb', 1, 324, 2); 5 GO
如果文中有不对的地方,欢迎大家拍砖o(∩_∩)o