【问题标题】:SQL Server 2012: Running multiple UPDATE FROM SELECT on the same table simultaneouslySQL Server 2012:同时在同一个表上运行多个 UPDATE FROM SELECT
【发布时间】:2014-07-24 22:38:25
【问题描述】:

情况:

1) 有很大的TABLE1(9GB 数据,20GB idx 空间,12M 行)
2) TABLE1 上有几个 UPDATEUPDATE/SELECT 一个一个运行
3) 每个 UPDATE 语句更新不同的列
4) 他们都没有使用以前更新的列来计算新的更新列
5) 完成它们需要一段时间

问题:

我想同时运行这些更新,但我担心死锁。如何避免它们? SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED会有帮助吗?

更新看起来像:

update TABLE1 set col1 = subs.col2
from (select ID, col2 from TABLE2) subs
where TABLE1.ID = subs.ID

update TABLE1 set col10 = col2+col3+col4

update TABLE1 set col100 = col2 + subs.col4
from (
    select 
        b.ID, a.col4
    from 
        TABLE3 a 
        join TABLE1 b on TABLE1.ID2 = TABLE3.ID2
) subs
where TABLE1.ID = subs.ID

update TABLE1 set col1000 = col2+col3+col4
from TABLE1
join TABLE4 on TABLE4.date = TABLE1.date
join TABLE5 on TABLE5.ID3 = TABLE1.ID 

【问题讨论】:

  • 这些更新会在一个会话中按顺序运行,还是多个会话会同时执行这些更新?我为什么要问这个问题;因为你提到了 ETL 过程。通常 ETL 进程在单个线程中运行。

标签: sql-server locking sql-update


【解决方案1】:

如果相同的列未更新且未在其他子句中使用,则带有 READ UNCOMMITTED 的脏读可能会起作用,但恐怕这是一个脆弱的解决方案。

要获得更一致的解决方案,您可以混合使用 ROWLOCK/UPDLOCK/NOLOCK 取决于操作。 F.e.

UPDATE
    TABLE1 WITH (ROWLOCK)
SET
    col1 = TABLE2.col2
FROM
    TABLE1 WITH (ROWLOCK, UPDLOCK)
    INNER JOIN TABLE2 WITH (NOLOCK) ON (TABLE1.ID = TABLE2.ID)

如果您的语句主要更新不同的行,则可以省略 ROWLOCK。

在极少数情况下,可能会发生锁升级,但可能会受到以下因素的限制

ALTER TABLE TABLE1 SET (LOCK_ESCALATION = DISABLE)

顺便说一句,您的解决方案的目的是什么?我不认为你会赢得很多性能,并且小的部分更新可以比并行的大更新更快地处理。

【讨论】:

  • 感谢您的努力,但由于存在全表扫描,您的解决方案仍会以死锁告终。
  • 如果您将在选择子句中使用 UPDLOCK,则扫描将获取更新->独占锁。两个线程不能共享它们,所以我认为你不会遇到死锁。已更新答案并提供更多详细信息。
  • 关于目的:它的数据仓库数据库和我正在更新的这个特定表是主要的销售表。对于每笔销售,都会计算一些值(如额外费用/成本)。所以基本上我运行的那几个更新都在更新大部分行。那里没有“小更新”。我拥有具有大量 RAM 和 CPU 内核的强大服务器,并行运行查询可以提高性能。顺便说一句:我什至在考虑 N 个临时表和 ETL 过程结束时的一个也是唯一一个巨大的更新。
  • 临时表可能会有所帮助,如果您使用 SELECT INTO 这是与更新相比记录最少的操作之一,即记录最多的操作(尤其是当您有并行更新时,因为如果更改错过了日志缓存线程将相同的记录写入 WAL 并因此产生更多的 IO 操作)。此外,大型更新的良好做法是将其拆分为较小的更新。因此,如果您有唯一标识符/时间戳或任何其他范围字段,最好将 12M 记录拆分为 10K-100K 块并相互更新。
【解决方案2】:

(1) 更新时避免子查询。多个子查询会很快导致锁升级并导致死锁。

(2) 查看TABLOCK vs TABLOCKX 的以下讨论。

(3) 对于当前的阻塞和锁定,请查看How to find out what table a page lock belongs to的讨论

【讨论】:

    【解决方案3】:

    另一种策略:创建一个临时表,其中包含要插入的行的 ID 以及列的新值。

    CREATE TABLE #tmp (
      RowID          int,
      NewCol1Value   ...,
      NewCol2Value   ...,
      NewCol2Value   ...
    )
    
    -- Insert into the tmp table
    ...
    
    UPDATE Table1
      SET  Col1 = ISNULL(NewCol1Value, Col1),
           Col2 = ISNULL(NewCol2Value, Col2),
           ...
      FROM Table1 INNER JOIN #tmp ON Table1.RowID = #tmp.RowID
    

    【讨论】:

      猜你喜欢
      • 2018-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多