笔记298 关于鬼影的翻译一
笔记298 关于鬼影的翻译一
1 关于鬼影的翻译一 2 http://www.sqlskills.com/blogs/paul/inside-the-storage-engine-ghost-cleanup-in-depth/ 3 很多年前,我在各种各样的论坛看到关于鬼影清理任务的帖子当我还在存储引擎开发团队的时候。 4 之前鬼影清除任务有一些bug在里面,KB文章是KB932115和KB815594,不过这两篇文章介绍鬼影机制的信息还是很少 5 由于某些原因,在我的旧的博客里面没有提及鬼影,今天我打算写一篇深入鬼影清理任务的文章 6 7 那么,什么是鬼影清理呢?这是一个后台进程用来清理鬼影记录的.通常与鬼影清理任务一起被提及. 8 什么是鬼影记录呢?上星期我曾经在anatomy OF a record post主题里简短描述了一下,在一张有索引的表里已经 9 删除了的一条记录或者在快照隔离级别的数据库里,一张没有任何索引的表里已经删除了的一条记录那条记录就是鬼影记录 10 11 从聚集索引表这方面讲述鬼影记录会好一些,鬼影就是一个delete操作,但是他实际不是从数据页进行物理删除,他只是 12 在数据页标记那条记录“已经删除了”或“已经鬼影了”。这对于删除操作的快速执行是一个性能的优化。 13 这也让删除操作的回滚执行得更快因为只需要把“已经删除了的”或“已经鬼影了的”的记录重新标记为“未删除“,这样就不用 14 重新插入已经删除了的记录。已经删除了的记录将会在稍后被一个后台的鬼影清理进程/任务物理删除(删除了的记录的那个位置, 15 不会被其他记录重写)。鬼影记录会在空的数据页面或者空的索引页面插入一条记录,以避免那个空的数据页面或者空的索引页面 16 被释放,当回滚事务的时候能保证那个数据页或索引页还存在 17 18 鬼影记录清理任务不能物理删除已经被标记为鬼影记录的的记录,除非你提交你的事务,确认删除操作已经提交,因为已经删除的记录 19 有锁,直到提交事务的时候才会解锁。顺便说一下,如果一条记录被标记为“已经删除了的”在一个数据页上,那么,就算加上nolock选项 20 或者使用READ UNCOMMITTED隔离级别也不能读取得到已经标记为“鬼影”的那条记录 21 22 当一条记录已经被删除,并且标记为鬼影记录,那么鬼影记录所在的那个数据页也会被标记为鬼影页面,在PFS页面会有所体现 23 并且信息会记录在数据页的页面头部。在PFS页记录当前存在鬼影记录的数据页,也能提醒数据库,当前数据库中存在一些鬼影记录 24 还没有去清理。鬼影记录清理任务不知道哪些数据页面有鬼影记录就算有删除操作发生。只有当扫描操作读取到已经标记为鬼影的 25 数据页面的时候。 26 27 28 鬼影记录清理任务每5秒钟进行一次查找鬼影记录的任务。扫描任务通过每个数据库的PFS页,查看是否有鬼影记录页面存在, 29 如果存在就通知鬼影清理任务清理鬼影记录页面里的鬼影记录。如果不存在,扫描任务就去扫描下一个数据库。 30 鬼影清理任务每次清理最多10个页面的鬼影记录,在我印象中好像是10个页面,以确保系统不会有任何影响。 31 所以,鬼影记录最终一定会被删除,通过鬼影清理任务。如果鬼影清理任务扫描到当前数据库没有鬼影记录,那么他就会 32 跳过这个数据库去扫描下一个数据库 33 34 您能够告诉鬼影记录清理任务是怎样工作的吗?在SQLSERVER2005里,您能够使用下面的代码在 35 sys.dm_exec_requests视图里查看鬼影清理任务 36 37 USE [tempdb] 38 GO 39 SELECT * 40 INTO ##myexecrequests 41 FROM sys.dm_exec_requests 42 WHERE 1 = 0; --where 1=0 只复制架构 43 GO 44 SET NOCOUNT ON; 45 GO 46 DECLARE @a INT 47 SELECT @a = 0; 48 WHILE ( @a < 1 ) 49 BEGIN 50 INSERT INTO ##myexecrequests 51 SELECT * 52 FROM sys.dm_exec_requests 53 WHERE command LIKE '%ghost%' 54 SELECT @a = COUNT(*) 55 FROM ##myexecrequests 56 END; 57 GO 58 SELECT * 59 FROM ##myexecrequests; 60 GO 61 DROP TABLE [##myexecrequests] 62 63 --如果是SQLSERVER2000 你需要使用 sysprocesses 64 --SQL2005也可以使用sysprocesses ,不过他也是继承自DMV 65 SELECT * 66 INTO mysysprocesses 67 FROM master.dbo.sysprocesses 68 WHERE 1 = 0; 69 GO 70 SET NOCOUNT ON; 71 GO 72 DECLARE @a INT 73 SELECT @a = 0; 74 WHILE ( @a < 1 ) 75 BEGIN 76 INSERT INTO mysysprocesses 77 SELECT * 78 FROM master.dbo.sysprocesses 79 WHERE cmd LIKE '%ghost%' 80 SELECT @a = COUNT(*) 81 FROM mysysprocesses 82 END; 83 GO 84 SELECT * 85 FROM mysysprocesses; 86 GO 87 88 在 sys.dm_exec_requests 输出的结果像下面这样 不过有很多列不需要用到的,需要去除掉 89 90 session_id request_id start_time status command 91 ———- ———– ———————– ———— —————- 92 15 0 2007-10-05 16:34:49.653 background GHOST CLEANUP 93 94 ----------------------------------------------------------------------------------- 95 那么,你告诉我,一条记录怎样变为ghost 记录的? 96 让我们让DBCC PAGE命令看一下数据页面里的内容吧 97 我会删除一些不是很重要的的位和高亮重要的鬼影记录部分 98 99 做下面实验之前确保您的数据库是 完整恢复模式 ,不能是简单恢复模式,所以不要选择tempdb来做实验 100 不然第八个步骤看不到PFS页的修改 101 102 先执行下面的sql语句 103 USE [GPOSDB] 104 GO 105 CREATE TABLE t1 ( c1 CHAR(10) ) 106 CREATE CLUSTERED INDEX t1c1 ON t1 (c1) 107 GO 108 BEGIN TRAN PaulsTran 109 INSERT INTO t1 VALUES ( 'PAUL' ) 110 INSERT INTO t1 VALUES ( 'KIMBERLY' ) 111 DELETE FROM t1 WHERE c1 = 'KIMBERLY'; 112 GO 113 114 DBCC IND ('GPOSDB', 't1', 1); 115 GO 116 ----------------------- 117 --补充一下:DBCC IND 里的page type列 118 PageType 119 Page type: 120 1 = data page, 121 2 = index page, 122 3 = LOB_MIXED_PAGE, 123 4 = LOB_TREE_PAGE, 124 10 = IAM page 125 126 ---------------------- 127 DBCC TRACEON (3604); 128 GO 129 DBCC PAGE ('gposdb', 1, 482, 3); 130 DBCC PAGE ('gposdb', 1, 482, 2); 131 132 让我们看一下在在删除动作的过程当中事务日志里发生了什么(请记住在测试数据库上做实验,不然后果自负) 133 我已经去除了大部分没有用的列 134 135 DECLARE @a CHAR(20) 136 SELECT @a = [Transaction ID] FROM fn_dblog(NULL, NULL) 137 WHERE [Transaction Name] = 'PaulsTran' 138 139 SELECT * FROM fn_dblog(NULL, NULL) 140 WHERE [Transaction ID] = @a; 141 GO 142 143 可以看到有两个插入然后跟着一个删除,删除记录标记为了鬼影记录 144 不过,在哪里更新PFS页呢? 145 更新PFS页里的鬼影位不是一个事务的一部分。我们需要用另一方法查找 146 除非在事务日志记录里dump所有的步骤并动手查找 147 148 149 SELECT Description, * FROM fn_dblog (null, null) 150 WHERE Context like '%PFS%' AND AllocUnitName like '%t1%'; 151 GO 152 153 首先PFS分配了一页,然后第二个就是我们要找的,他为了告诉大家有鬼影记录 154 就改变了位图中的位。 155 下面提交事务,看一下有什么事发生,过滤掉先前的事务日志 156 ------------------------------------------------------ 157 --先执行下面这句,看一下最大的LSN的LSN号是多少 158 SELECT MAX ([Current LSN]) FROM fn_dblog (null, null); 159 GO 160 – 00000014:000000fc:020c 161 ----------------------------------------------------- 162 --然后提交事务 163 COMMIT TRAN 164 GO 165 SELECT [Page ID], * FROM fn_dblog (null, null) WHERE [Current LSN] > '00000093:000004e8:0025'; 166 167 168 169 我们看到几乎是只要提交事务,鬼影记录清理任务就会进行清理工作。 170 PFS页也进行了位图中位的改变,因为鬼影记录已经没有了,所以他也要改变位图中相应的位 171 现在检查一下页面dump信息确保记录已经删除掉了,还有页面的头部信息不再显示 172 鬼影记录了 173 174 DBCC PAGE ('gposdb', 1, 324, 3); 175 GO 176 177 DBCC PAGE ('gposdb', 1, 324, 2); 178 GO 179 180 就算记录已经不再存在了,但是那条记录还是会保存在数据页面的末尾 181 直到数据页面的空间被重用,被其他记录重写