首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何避免表锁升级?

如何避免表锁升级?
EN

Database Administration用户
提问于 2020-09-30 11:02:15
回答 1查看 1.3K关注 0票数 7

我有一项任务要更新生产表中的500万行,而不需要将整个表锁定很长时间

因此,我使用了以前帮助过我的方法--每次更新顶部(N)行,在块之间间隔1-N秒。

这一次从每次更新top (1000)行开始,监视进程中lock_escalation事件的扩展事件会话。

lock_escalation在每次更新操作中都会出现,所以我开始将每块1000 -> 500 -> 200 -> 100 -> 50行的行数降低到1。

在此之前(不使用此表,对于删除操作--而不是更新),将行数降低到200或100,有助于消除lock_escalation事件,但这一次,即使每次更新操作有1行,表lock_escalation仍然会出现。每个更新操作的持续时间大致相同,无论每次是1行还是1000行。

在我的情况下,如何处理表锁的升级?

@@TRANCOUNT为零

延长活动:

代码

代码语言:javascript
复制
set nocount on

declare 
    @ChunkSize              int = 1000,                         -- count rows to remove in 1 chunk 
    @TimeBetweenChunks      char(8) = '00:00:01',               -- interval between chunks
    
    @Start                  datetime,
    @End                    datetime,
    @Diff                   int,
    
    @MessageText            varchar(500),
    
    @counter                int = 1,
    @RowCount               int = 1,
    @TotalRowsToUpdate      bigint,
    @TotalRowsLeft          bigint
    


-- total row count to update
set @TotalRowsToUpdate = (select count(*)
                            from [Table1]
                                join [Table2] on
                                    btid = tBtID
                            where   btStatusID = 81)


set @TotalRowsLeft = @TotalRowsToUpdate
set @MessageText = 'Total Rows to Update = ' + cast(@TotalRowsLeft as varchar) raiserror (@MessageText,0,1) with nowait
print ''



-- begin cycle
while @RowCount > 0 begin

    set @Start = getdate()

    -- update packages
    update top (@ChunkSize) bti
        set btstatusid = 154,
            btType = 1
    from [Table1] bti
        join [Table2] on
            btid = tBtID
    where   btStatusID = 81
    

    set @RowCount = @@ROWCOUNT

    -- measure time
    set @End = getdate()
    set @Diff = datediff(ms,@Start,@End)

    set @TotalRowsLeft = @TotalRowsLeft - @RowCount
    set @MessageText = cast(@counter as varchar) + ' - Updated ' + cast(@RowCount as varchar) + ' rows in ' + cast(@Diff as varchar) + ' milliseconds - total ' + cast(@TotalRowsLeft as varchar) + ' rows left...'

    -- print progress message
    raiserror (@MessageText,0,1) with nowait


    set @counter += 1

    WAITFOR DELAY @TimeBetweenChunks

end

计划:

https://www.brentozar.com/pastetheplan/?id=SyozGWMLw

EN

回答 1

Database Administration用户

回答已采纳

发布于 2020-09-30 15:08:41

如果我们查看实际计划,当前的查询将从表中读取太多要更新的数据。这来自于BoxTrackInfo上的索引搜索:

这是在btid上查找BlueTrackEvents扫描后出现的每一行的索引。在检查btStatusID时获取更新锁,以查看行是否符合更新的条件。只有1 401行符合更新的条件,但在进程中使用了更多的锁,从而导致锁升级到表级别。

您确实需要一种不同的计划形式--在btStatusID上查找D6表,然后加入到BlueTrackEvents中,这样可以获得更少的锁。为此,添加这样的索引应该会有所帮助:

代码语言:javascript
复制
CREATE NONCLUSTERED INDEX IX_btStatusID 
ON dbo.BoxTrackInfo (btStatusID)
INCLUDE (btType);

这样可以更有效地定位符合条件的行,希望能够在不升级锁的情况下完成更新。

另外,当前的执行计划使用合并半连接验证btStatusID上的外键约束:

在您的情况下,这可能不是什么大问题,因为LBoxTrackStatus表中只有267行。如果该表更大,您可以考虑在查询中添加一个LOOP JOINFAST 1提示,以获得嵌套循环FK验证。详情请参阅本帖:

为什么在插入上出现快照隔离问题?

票数 9
EN
页面原文内容由Database Administration提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://dba.stackexchange.com/questions/276314

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档