存储引擎揭秘:深入理解幽灵清除

存储引擎揭秘:深入理解幽灵清除


原文地址:

http://www.sqlskills.com/BLOGS/PAUL/post/Inside-the-Storage-Engine-Ghost-cleanup-in-depth.aspx

(译注: MSND 中将 ghost 翻译为虚影,本文翻译为幽灵,一个意思。)

正文:

在存储引擎小组的这几年,我看到很多关于幽灵清除的帖子。这个程序的以前版本中有一些bugKB932115KB815594 ),而且关于它的信息很少。基于某些原因,我没有再以前的博文中讲述它,今天我想详细介绍它。

那么什么是幽灵清除呢?它是一个用来清除幽灵的后台程序,通常也被称为幽灵清除任务。什么是幽灵呢?正如上周我在《  存储引擎揭秘:基本结构之一 ——记录 》中进行简单描述的一样,幽灵是指刚被删除的索引记录。(实际上,如果是快照隔离级别可能更复杂,但就目前而言,说“索引记录”已经可以了。)这样的一个删除操作并不会将记录从页中物理删除——它只是将记录标明已经删除了(或者说变成幽灵了)。这种性能优化措施可以让删除更快速地完成,它也可以快速地回滚删除操作,因为要做的动作仅仅是将删除标志改回去,而不是重新插入被删除的记录。那条被删除的记录以后会被后台运行的幽灵清除任务物理清除。为了防止释放空的数据和索引页,幽灵清除任务会在页上留下一个记录(译注:这是在 SQL SERVER 2000 下, 2005 及以后版本不是这样了)。

 

幽灵清除任务直到删除事务被提交后才能物理删除这些幽灵记录,因为提交事务前,被删除记录是被锁住的,而且锁直到事务被提交才释放。顺便说一下,如果一个页中有幽灵记录,即使是 NOLOCK READ UNCOMMITTED 方式下也不会返回这些幽灵记录,因为它们被标为幽灵记录了。

 

当一个记录被删除,除了将这个记录标为幽灵记录,记录所在页的头部以及分配该页的 PFS 中都会标明。在 PFS 页上标明某页有幽灵记录也会改变数据库的状态——标明数据库中某处有幽灵记录需要清除。当然系统是没有办法告诉幽灵清除任务哪些页发生了删除操作。只有下次扫描操作读到了这些页才会注意到这些页中有幽灵记录。(译注:根据上下文意思,“下次扫描”是指幽灵清除任务扫描数据库中的所有 PFS 页。)

 

幽灵清除任务不是一请求就运行,它是每 5 秒钟自动在后台运行来查找幽灵记录来清除。记住幽灵清除任务并不是由删除操作来指示清除一个指定页的——它是由后来的扫描操作指示的。当幽灵清除任务开始时,它会检查数据库中是否有页需要被清除,若有则清除。若没有,则选择标明有幽灵记录的数据库,扫描数据库中的所有 PFS 页检查是否有页需要清除。为了确保不会加重系统负担,每次运行时,幽灵清除任务只会检查并清除有限数量的页——我记得好像是 10 页。如果它处理了数据库且没有再发现任何幽灵记录,它就会将数据库标明无幽灵记录,这样下次就可以跳过检查了。

 

你如何辨别幽灵清除任务是否正在运行呢?在 SQL SERVER 2005 中,你可以运行下面的代码,在 sys.dm_exec_requests 中查看幽灵清除任务:

SELECT * INTO myexecrequests FROM sys.dm_exec_requests WHERE 1 = 0 ;

GO

SET NOCOUNT ON ;

GO

DECLARE @a INT

SELECT @a = 0 ;

WHILE ( @a < 1 )

BEGIN

INSERT INTO myexecrequests SELECT * FROM sys.dm_exec_requests WHERE command LIKE '%ghost%'

SELECT @a = COUNT (*) FROM myexecrequests

END ;

GO

SELECT * FROM myexecrequests ;

GO

SQL Server 2000 中你需要使用sysprocesses( 当然,在SQL Server2005 也可以使用它,但是它是从DMV 中继承来的假视图):

SELECT * INTO mysysprocesses FROM master . dbo . sysprocesses WHERE 1 = 0 ;

GO

SET NOCOUNT ON ;

GO

DECLARE @a INT

SELECT @a = 0 ;

WHILE ( @a < 1 )

BEGIN

INSERT INTO mysysprocesses SELECT * FROM master. dbo. sysprocesses WHERE cmd LIKE '%ghost%'

SELECT @a = COUNT (*) FROM mysysprocesses

END ;

GO

SELECT * FROM mysysprocesses ;

GO

sys.dm_exec_requests 输出如下(去除了大部分没用的和无意义的列):

session_id request_id  start_time              status       command
---------- ----------- ----------------------- ------------ ----------------
15         0           2007-10-05 16:34:49.653 background   GHOST CLEANUP

那么你是如何判断一个记录是幽灵记录呢?让我们搭建一个环境然后使用DBCC PAGE 来查看——我已经去除了无意义的输出,并且将感兴趣的幽灵记录部分加深:

CREATE TABLE t1 ( c1 CHAR ( 10 ))

CREATE CLUSTERED INDEX t1c1 on t1 ( c1 )

GO

BEGIN TRAN PaulsTran

INSERT INTO t1 VALUES ( 'PAUL' )

INSERT INTO t1 VALUES ( 'KIMBERLY' )

DELETE FROM t1 WHERE c1 = 'KIMBERLY' ;

GO

DBCC IND ( 'ghostrecordtest' , 't1' , 1 );

GO

DBCC TRACEON ( 3604 );

GO

DBCC PAGE ( 'ghostrecordtest' , 1 , 143 , 3 );

GO

<snip>

m_freeData = 130    m_reservedCnt = 0   m_lsn = (20:88:20)
m_xactReserved = 0  m_xdesId = (0:518)  m_ghostRecCnt = 1
m_tornBits = 0

<snip>

Slot 0 Offset 0x71 Length 17

Record Type = GHOST_DATA_RECORD       Record Attributes =  NULL_BITMAP
Memory Dump @0x6256C071

00000000:   1c000e00 4b494d42 45524c59 20200200 †....KIMBERLY  ..
00000010:   fc†††††††††††††††††††††††††††††††††††.
UNIQUIFIER = [NULL]

Slot 0 Column 1 Offset 0x4 Length 10

c1 = KIMBERLY

Slot 1 Offset 0x60 Length 17

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP
Memory Dump @0x6256C060

00000000:   10000e00 5041554c 20202020 20200200 †....PAUL      ..
00000010:   fc†††††††††††††††††††††††††††††††††††.
UNIQUIFIER = [NULL]

Slot 1 Column 1 Offset 0x4 Length 10

c1 = PAUL

让我们来看看在这个过程中事务日志是怎样的(记住这是非文档化的且不提供支持——确保只在测试数据库上执行)?我已去除了很多无意义列:

DECLARE @a CHAR ( 20 )

SELECT @a = [Transaction ID] FROM fn_dblog (null, null) WHERE [Transaction Name] = 'PaulsTran'

SELECT * FROM fn_dblog (null, null) WHERE [Transaction ID] = @a ;

GO

Current LSN              Operation         Context             Transaction ID
------------------------ ----------------- ------------------- --------------
00000014:00000054:0011   LOP_BEGIN_XACT    LCX_NULL            0000:00000206
00000014:0000005a:0012   LOP_INSERT_ROWS   LCX_CLUSTERED       0000:00000206
00000014:0000005a:0013   LOP_INSERT_ROWS   LCX_CLUSTERED       0000:00000206
00000014:0000005a:0014   LOP_DELETE_ROWS   LCX_MARK_AS_GHOST   0000:00000206

在两条删除插入动作后跟着一条删除动作——删除的行被标为幽灵记录。但是哪儿是对PFS 页的更新呢?哈哈,在PFS 页中改变ghost 位并不是事务的一部分。我们需要通过其他途径来观察它(除了列出所有的事务日志并人工检查):

SELECT Description , * FROM fn_dblog (null, null) WHERE Context like '%PFS%' AND AllocUnitName like '%t1%' ;

GO

Description               Current LSN              Operation        Context   Transaction ID
------------------------- ------------------------ ---------------- --------- ----------------
Allocated 0001:0000008f   00000014:00000054:0014   LOP_MODIFY_ROW   LCX_PFS   0000:00000208
                          00000014:0000005a:0015   LOP_SET_BITS     LCX_PFS   0000:00000000

第一条是分配一个页,但是第二条正是我们寻找的——它修改了页中的位:表明页中有幽灵记录。现在让我们提交事务,再看看发生什么吧,过滤掉所有先前的事务日志:

SELECT MAX ( [Current LSN] ) FROM fn_dblog (null, null);

GO

-- 00000014:0000005e:0001

COMMIT TRAN

GO

SELECT [Page ID] , * FROM fn_dblog (null, null) WHERE [Current LSN] > '00000014:0000005e:0001' ;

GO

Page ID         Current LSN              Operation          Context         Transaction ID
--------------- ------------------------ ------------------ --------------- --------------
NULL            00000014:0000005f:0001   LOP_COMMIT_XACT    LCX_NULL        0000:00000206
0001:0000008f   00000014:00000060:0001   LOP_EXPUNGE_ROWS   LCX_CLUSTERED   0000:00000000

我们看到几乎是事务一被提交,幽灵清除任务就处理了该页。我们列出页并检查一下:页确实已被删掉了,但记录的内容还在(无关信息也已被删除)。

DBCC PAGE ( 'ghostrecordtest' , 1 , 143 , 3 );

GO

<snip>

m_freeData = 130         m_reservedCnt = 0        m_lsn = (20:94:1)
m_xactReserved = 0       m_xdesId = (0:518)       m_ghostRecCnt = 0
m_tornBits = 0

<snip>

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP
Memory Dump @0x6212C060

00000000:   10000e00 5041554c 20202020 20200200 †....PAUL      ..
00000010:   fc†††††††††††††††††††††††††††††††††††.
UNIQUIFIER = [NULL]

Slot 0 Column 1 Offset 0x4 Length 10

c1 = PAUL

DBCC PAGE ( 'ghostrecordtest' , 1 , 143 , 2 );

GO

<snip>

6212C 040:   01000000 00000000 00000000 00000000 †................
6212C050:   00000000 00000000 00000000 00000000 †................
6212C060:   10000e00 5041554c 20202020 20200200 †....PAUL      ..
6212C070:   fc1c000e 004b494d 4245524c 59202002 †.....KIMBERLY   .
6212C 080:   00fc0000 00000000 00000000 01000000 †................
6212C090:   00000000 13000000 01000000 00000000 †................

<snip>

所以即使记录已经不存在了,所做的仅仅是 slot 从行偏移表中删除了——记录的内容还在,直到空间被再次使用为止。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 测试概述 本次测试针对深海幽灵z3air笔记本电脑的软件进行测试,主要包括操作系统、应用软件、驱动程序等方面的测试,目的是排除软件存在的问题,确保深海幽灵z3air笔记本电脑的软件稳定性和可靠性。 2. 测试环境 操作系统:Windows 10 Home 64-bit 处理器:Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz 2.59 GHz 内存:16.0 GB (15.4 GB 可用) 显卡:NVIDIA GeForce GTX 1660 Ti with Max-Q Design 硬盘:1TB SSD 3. 测试内容及方法 3.1 操作系统 3.1.1 安装测试:通过对深海幽灵z3air笔记本电脑进行操作系统的安装测试,检查操作系统安装时是否存在异常情况。 3.1.2 启动测试:通过多次启动深海幽灵z3air笔记本电脑,检查操作系统启动是否稳定,是否存在死机、蓝屏等异常情况。 3.1.3 稳定性测试:通过运行多个程序,同时使用繁重的操作来测试操作系统的稳定性,如打开多个浏览器标签页、多个应用程序、同时进行文件复制等。 3.2 应用软件 3.2.1 功能测试:对深海幽灵z3air笔记本电脑预装的各种应用软件进行功能测试,包括浏览器、音乐播放器、笔记本、邮箱等。 3.2.2 稳定性测试:通过运行多个应用程序,同时使用繁重的操作来测试应用软件的稳定性,如打开多个浏览器标签页、同时播放多个视频、同时打开多个应用程序等。 3.3 驱动程序 3.3.1 安装测试:对深海幽灵z3air笔记本电脑的各种驱动程序进行安装测试,包括显卡驱动、网卡驱动、声卡驱动等。 3.3.2 稳定性测试:通过运行多个应用程序,同时使用繁重的操作来测试驱动程序的稳定性,如打开多个浏览器标签页、同时播放多个视频、同时打开多个应用程序等。 4. 测试结果 4.1 操作系统测试结果:操作系统安装、启动、稳定性等方面都表现正常,不存在异常情况。 4.2 应用软件测试结果:预装软件的功能正常,运行稳定性良好,未出现崩溃或死机情况。 4.3 驱动程序测试结果:驱动程序安装正常,运行稳定性良好,未出现崩溃或死机情况。 5. 测试结论 通过本次测试,深海幽灵z3air笔记本电脑的软件表现稳定可靠,不存在严重问题,用户可以放心使用。建议在日常使用过程中,要注意避免多开过多的应用程序,以保证笔记本电脑的性能和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值