首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >最小化表锁的持续时间

最小化表锁的持续时间
EN

Database Administration用户
提问于 2022-05-17 14:54:46
回答 1查看 469关注 0票数 5

我有一个SQL表,需要每天更新。在更新过程中,可能会对该表进行查询,也可能不会。大约500,000行。

当要更新表的作业与对表的查询同时运行时,会出现锁定冲突的问题。

因此,我重写了更新表的过程如下:

代码语言:javascript
运行
复制
ALTER procedure [dbo].[Table_Generate] as

declare @d datetime = getdate(), @c as int

--Check temp tables
IF OBJECT_ID('tempdb..#final') IS NOT NULL
    DROP TABLE #final

IF OBJECT_ID('tempdb..#base') IS NOT NULL
    DROP TABLE #base

--Get source data from linked server
select
    ID, 
    Reference, 
    StartDate, 
    EndDate, 
    Description,
    SomeCode
into #base


from    [LinkedServer].[Database].dbo.[A_View]

--Generate row_hash
select 
    ID, 
    Reference, 
    StartDate, 
    EndDate, 
    Description,
    SomeCode,
    hashbytes('SHA2_256',(
     select
        ID, 
        Reference, 
        StartDate, 
        EndDate, 
        Description,
        SomeCode 
     from #base sub where sub.ID = main.ID for xml raw)) as row_hash
    into #final
    from #base main

select @c = count(*) from #final
if @c >0 begin

merge [The_Table_Staging] as target
using #final as source
on source.ID = target.ID

--New rows
when not matched by target then
insert (    RunDate,
            ID, 
            Reference, 
            StartDate, 
            EndDate, 
            Description,
            SomeCode,
            Row_Hash
        ) values (
            @d,
            source.ID, 
            source.Reference, 
            source.StartDate,
            source.EndDate, 
            source.Description,
            source.SomeCode,
            source.row_hash)

--Existing changed rows
when matched and source.row_hash != target.row_hash then update set
     target.RunDate         = @d
    ,target.Reference       = source.Reference
    ,target.StartDate       = source.StartDate
    ,target.EndDate         = source.EndDate
    ,target.Description     = source.Description
    ,target.SomeCode        = source.SomeCode
    ,target.row_hash        = source.row_hash

--Deleted rows
when not matched by source then delete;

--Existing unchanged rows
update [The_Table_Staging] set RunDate = @d where RunDate != @d

--Commit changes
begin transaction

exec sp_rename 'The_Table_Live', 'The_Table_Staging_T'
exec sp_rename 'The_Table_Staging', 'The_Table_Live'
exec sp_rename 'The_Table_Staging_T', 'The_Table_Staging'

commit transaction
end

这样做的目的是减少不必要的行更新,并尽量减少活动表的锁定。我不太喜欢做表重命名,但是执行更新/插入需要5-10秒,而表重命名几乎是瞬间的。

所以我的问题是:这种方法可以吗?或者我可以改进它?

谢谢!

编辑对JD

的响应

嗨,JD,请不要道歉--我是来接受建设性的批评的。

  1. 我不知道MERGE有问题。我自己也从来没出过问题,但是谢谢
  2. 我可以将这个部分重写成单独的INSERT/UPDATE/DELETE语句。
  3. 我通常同意。我这样做的原因是,如果我在这一点上做了一个TRUNCATE/INSERT,它需要6-10秒,而sp_rename需要不到1秒。所以锁表的时间更短了
  4. 这不影响表锁定,因为它首先将数据带入暂存表中。我别无选择,只能使用链接服务器或SSIS,在这种情况下,我更喜欢链接服务器将所有SQL保存在一个地方。
  5. 我总是使用XML而不是CONCAT,因为否则'a','bc‘将与'ab','c’一样散列,这是不正确的。

从暂存到填充活动表的所有处理都很好--我只想尽量减少最后的活动表锁定的时间,

EN

回答 1

Database Administration用户

回答已采纳

发布于 2022-05-17 18:49:40

不幸的是,在您的代码中,我一眼就看到了许多错误:

  1. 在生产代码中,确实应该避免大量的已知的bugMERGE语句
  2. 众所周知,MERGE的性能不如编写单独的INSERTUPDATEDELETE语句,这可能是导致某些阻塞问题的原因。
  3. 虽然很诱人,但是使用sp_rename函数来最小化阻塞问题实际上会导致更糟糕的阻塞问题,正如Kendra的为什么要切换暂存表而不是重命名它们中所讨论的那样。(如果我没有记错的话,这将讨论使用分区切换作为更好的解决方案。)
  4. 链接的服务器有时也是性能瓶颈 (用于固定基数估计,并在处理之前将所有数据带过网络)。如果您以前在代码事务中依赖它,则可以将该部分保留在事务之外,以尽量减少本地表的锁定时间。
  5. 另外,我也喜欢使用HASHBYTES()函数来生成行哈希,这是我过去使用过的。但是,您还可能会发现在行本身上调用它的性能更好,方法是在每个列上使用CONCAT(),其中包含一个安全的列分隔符(例如,双管||)作为参数,而不是在利用XML的子查询中使用它。
  6. 话虽如此,正如埃里克·达林( Erik ),HASHBYTES()它本身有时容易受到规模上的性能问题的影响。所指出的。如果这是您的瓶颈,您可以尝试在计算列或索引视图(它是确定性的)中实现函数的结果,或者使用另一种方法,如链接post中提到的CLR。
票数 8
EN
页面原文内容由Database Administration提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

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

复制
相关文章

相似问题

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