如何以最少的停机时间从大型(700M 行)热(每秒多个事务)表中删除列?

Cyn*_*ker 4 sql-server locking sql-server-2016

我的研究表明,我可以在短时间内删除表中的一列(前提是我获得了排他锁),因为这只是元数据更改。

该表有两个不同的服务,不断插入和更新记录。太热了。如果不开发这种活动级别的完整复制环境,我怎么能确保像这样的陈述

ALTER TABLE x DROP COLUMN y 
Run Code Online (Sandbox Code Playgroud)

会成功获得排他锁,快速做drop,然后解锁表吗?

SQL Server 将如何将此请求排队(例如,先进先出)?我能确定 DROP COLUMN 真的只需要几分钟吗?

在 PROD 中长时间锁定此表是不可接受的,因此我试图避免出现意外。

其他注意事项:稍后我将在线重新索引以对索引进行碎片整理并回收空间。

Dav*_*oft 8

SQL Server 将如何将此请求排队(例如,先进先出)?我能确定 DROP COLUMN 真的只需要几分钟吗?

ALTER TABLE 的锁定方式是您需要一个架构修改锁 (Sch-M) 来执行 ALTER。涉及该表的每个其他查询都需要架构稳定性锁 (Sch-S)。Sch-M 和 Sch-S 不兼容。

此外,在您等待获取 Sch-M 锁时,您会阻止对 Sch-S 锁的任何请求。因此,您等待所有进行中的查询和事务释放表上的 Sch-S 锁,并在等待时阻止所有新查询。这是 DDL 锁定的正常工作方式,只要您的表上没有任何长时间运行的查询,它就可以正常工作。

但是,如果您有长时间运行的查询,而您的 DDL 可能会卡在后面,那么您的 DDL 反过来会阻止所有新查询。因此,在繁忙的系统上运行 DDL 并同时进行短期和长期运行是一个问题。

低优先级等待通过允许您的 DDL 语句以降低的优先级等待,而不是阻止新查询来解决这个问题。DDL 会话将等待一个“窗口”,在该窗口中没有其他会话对表有任何锁定,然后运行。但由于 DDL 会话不会阻止新查询,因此它可能会永远等待。

不幸的是,没有为删除列启用低优先级等待。您可以创建一个反馈项目以要求在此处实施。已启用在线索引重建和分区切换。

大多数情况下,您可以使用 LOCK_TIMEOUT来模拟此行为并重试。因此,请使用锁定超时配置您的 DDL 会话,这样如果无法快速获取锁定,它将放弃 ALTER TABLE。

所以像这样运行alter:

set lock_timeout 1000 --1 second
alter table SomeTable
drop column if exists ColumnToDrop 
Run Code Online (Sandbox Code Playgroud)

它要么很快成功,要么因锁定超时错误而失败。如果您想在无人值守的作业中运行它,您可以使用重试循环继续尝试,直到 ALTER TABLE 快速成功。

像这样的东西:

set lock_timeout 1000 --1 second
declare @tries int = 0

while 1=1
begin 
    begin try
        set @tries = @tries + 1;

        alter table SomeTable
        drop column if exists ColumnToDrop 

        print 'complete'
        break;
    end try
    begin catch
      if @tries > 10 or error_number() <> 1222 throw;

      declare @msg nvarchar(2000) = concat('retry ', @tries);
      print @msg
    end catch
end
Run Code Online (Sandbox Code Playgroud)