SQL Server 如何删除记录
SQL Server 如何删除记录
引言
我惊讶于许多开发人员不知道 SQL Server 不会立即执行 DELETE 命令,而是会定期在后台线程中执行一个名为 GHOST CLEANUP 的进程。当事务请求删除行时,该行只是被标记为“幽灵”(ghosted),然后由后台进程实际删除数据。 这样可以提高性能,并且在 ROLLBACK 时可以减少工作量。 在系统表 sys.dm_exec_requests
中,您可以定期看到 GHOST CLEANUP 作为命令出现。
所以,让我们看看 SQL Server 在执行时实际做了什么:
DELETE FROM MyTable WHERE PrimaryKeyField = some_value
首先,让我们创建一个测试表并插入几条记录。
CREATE TABLE [dbo].[DeleteInternals](
[Id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[Name] [char](4) NOT NULL
)
GO
insert into DeleteInternals([Name]) values('Row1')
insert into DeleteInternals([Name]) values('Row2')
insert into DeleteInternals([Name]) values('Row3')
insert into DeleteInternals([Name]) values('Row4')
为了发现表的内部结构,我们将使用未文档化的命令 DBCC IND 和 DBCC PAGE,这些命令在 SQL Server 内部被广泛使用,但它们没有文档且不受支持,这意味着您使用它们需要自行承担风险。
要找出使用 DBCC PAGE 查看哪些页面 ID,我们执行:
DBCC IND(TestDatabase, DeleteInternals, 1)
输出中对我们来说重要的字段是:
- PageFID - 页面的文件 ID
- PagePID - 文件中的页码
- OBjectID - 页面所属对象的 ID
- IndexID - 页面所属索引的 ID
- PageType - 其中 1 = 数据页,2 = 索引页
- IndexLevel - 由于 SQL Server 在 B 树中使用双向链表来存储索引,因此数据实际上是聚簇索引的叶子级别(level 0)。
为了将输出返回到控制台,我们需要启用 T3604:
DBCC TRACEON(3604)
GO
DBCC PAGE(TestDatabase, 1,399,1)
这是此命令的输出:
对于我们的示例,以下记录很重要(用红色方框标记):
m_slotCnt = 4
- 显示页面上的记录数m_GhostRecCnt = 0
- “幽灵”记录的数量RecordType = PRIMARY_RECORD
用于 Slot1,其中包含稍后将被删除的“Row2”数据- Row - Offset 表 - 所有记录的偏移量。
执行 DELETE 命令后,我们将再次查看页面结构。 但是,为了防止 GHOST CLEANUP 进程执行并清理我们的页面,我们将把 DELETE 命令包含在一个事务中,并且在一段时间内不提交该事务:
begin transaction
delete from DeleteInternals where Id = 2
让我们看一下页面内容
DBCC PAGE(TestDatabase, 1,399,1)
我们仍然有 4 条记录 (m_slotCnt),但现在我们可以看到我们有一条“幽灵”记录,m_ghostRecCnt = 1。 该记录正是我们的“Row2”,现在被标记为 GHOST_DATA_RECORD (RecordType)。 Row - Offset 表保持不变。
提交事务后,通过执行:
commit
并再次检查页面结构:
DBCC PAGE(TestDatabase, 1,399,1)
我们看到,提交事务后,记录数等于 3 (m_slotCnt = 3),并且“幽灵”记录数等于 0 (m_ghostRecCnt = 0)
此外,Row - Offset 表显示所有三条记录,但您会注意到它们的偏移量与之前相同,即在执行删除命令之前,这意味着 SQL Server 没有压缩页面。 我们下次再讨论这个问题。