管理和删除大表中的数据

Lui*_*ões 4 sql-server deadlock delete table

我有以下场景:

一张巨大的桌子,也是目前系统中使用最多的桌子。我所说的巨大是指它的行数很大,实际磁盘大小也很大,因为它有 2 个字节的数组列,可以从我们的系统中保存文件。

问题是当我需要从中删除大量行时。首先,我只尝试了一个 delete within语句,但是当我决定重新启动数据库时,它冻结了整个表(不使用系统)大约 45 分钟。

然后我尝试删除 10 个注册表的块,并尝试删除 8k+ 行。它真的很慢,从那个表中获取数据的地方也很慢,当它删除了大约 700 行时,我启动了另一个使用同一个表的进程,并出现了死锁。

我应该如何从该表中进行这些批量删除?我考虑将表分成 2 个,一个用于包含大部分表卷的 2 字节数组,另一个用于包含其余信息(这些简单的东西,如 varchars、整数、日期时间等)。在这些删除情况下,这样做对我有好处吗?或者行数仍然会导致表上的这些锁?

SQL 的版本是 Microsoft SQL Server Standard(64 位)12.0.5000.0。

Aar*_*and 5

首先介绍几篇文章:

您没有提到 SQL Server 的版本,所以我不确定第二个是否与您相关,但可能与其他读者相关。我将专注于前者。

通常,删除大量数据会导致多种症状:

  1. 受影响的行数越多 = 锁升级的可能性越大
  2. 更长的事务 = 冗长的阻塞(有时会出现死锁,正如您所发现的)
  3. 大量写入事务日志 = 写入延迟(包括增长事件)

所以我在那篇文章中的想法是通过在“块”中删除来锁定更少的行 - 每个删除操作(或其中的有限数量)都将在一个事务中,从而减少锁定行的数量、事务的长度,以及对日志的影响。

所以我可能建议尝试这样的事情:

SELECT key_column
  INTO #work
  FROM dbo.big_table
  WHERE -- however you identify rows to delete;

CREATE CLUSTERED INDEX x ON #work(key_column);

DECLARE @rc  int = 1, 
  @counter   int = 1,
  @batchsize int = 100; -- this may or may not be an optimal chunk size

WHILE @rc > 0
BEGIN
  BEGIN TRANSACTION;

  DELETE TOP (@batchsize) t
    FROM dbo.big_table AS t
    INNER JOIN #work AS x
    ON t.key_column = x.key_column;

  SET @rc = @@ROWCOUNT;

  COMMIT TRANSACTION;

  SET @counter = @counter + 1;

  IF @counter % 10 -- or maybe 100 or 1000
  BEGIN
    CHECKPOINT; CHECKPOINT;
  END
END
Run Code Online (Sandbox Code Playgroud)

检查点确保可以重用事务中的日志空间(第二个确保日志正确“环绕”)。在您的完整恢复环境中,您只需要一个BACKUP LOG命令......或者您可以考虑切换到简单,如果您可以将此维护工作与您可以重新启动日志链的定期安排的完整备份非常接近。

诀窍是找到平衡日志重用和检查点或日志备份必须做的工作的最佳点。(在我的文章中,检查点的频率实际上导致整体操作变慢,这可能没问题,如果它真的像后台进程一样,这需要多长时间并不重要 - 问题不是持续时间,而是干扰。我只是没有四处寻找最佳位置。)

批处理大小有效地限制了任何给定事务中锁定的行数,并减少了该事务所需的工作量。如果你有足够现代的版本,你可以在这里使用延迟耐久性;最大的风险是您丢失了一笔交易,但由于您“丢失”的是数据删除,因此您并没有真正丢失任何东西。