表更新非常慢

Very Slow Updates on tables

我们正在 Azure DWH(现在是 Synapse)中复制现有解决方案,它加载增量数据并有一个设置为 0 的标志,以指示数据需要“处理”到 DIMS 和 FACTS 中。处理后,表将添加到需要重置的列表中。

晚上的维护工作 运行s 通过列表和 运行s

UPDATE xxxx SET FLAG_COLUMN = 1 WHERE FLAG_COLUMN = 0

我在这方面的表现非常参差不齐。 18 亿行表在 5m 内更新,较小的 700m 行表需要将近一个小时。几乎所有的表都是 COLUMN STORES。我尝试将更新简化为

UPDATE xxxx SET FLAG_COLUMN = 1 

我希望这对于列式存储来说会非常快,因为它会刷新整个列,但这似乎在列式存储和堆之间没有任何有意义的区别。每天有1800张桌子需要重置。 运行 一次这 40 个仍然需要 运行 2-3 小时以上才能以我达到的最佳速度进行重置。对于正在爬行的查询,它在一天内是无法实现的。

所有这些都是在 运行ning 环境安静的情况下进行的,因此这不是其他查询干扰的问题。我还没有探索过改变资源 class 但它 运行 下的帐户是 StaticRC40 并且似乎 运行 ADF 驱动的加载方式比在这个级别更新的要快得多并行度(在查询方面)。

有人有什么建议吗?我可能会尝试的其他事情的想法?这些表的大小从 100k 到 180 亿行不等(谢天谢地,大多数都在 10m 以内)我们正在 运行 以 DW3000c 的规模设置实例,并且它在我们 运行 的大多数其他东西上足够快。

这些相对简单的更新似乎最终是次优的。任何建议将不胜感激

非常感谢

事实证明这非常简单。我们没有重现现有行为。

在迁移中大约有。 1800 ODS tables,它们都有 ROW_SENT_TO_EDW 标志,但其中有大量从未设置过此标志,因此所有行都是 0。“慢”tables 是所有行都为 0 的行,因此更新针对的是数亿行。这证实了 UPDATE 的 TRANSACTION 日志记录是缓慢的根本原因,随机性与我们必须更新的行数有关。 (我仍然不明白为什么为 COLUMNSTORE INDEX table 设置整个列 = 0 不是很快)

运行 一些分析并查看使用 HEAPS 而不是 CLUSTERED COLUMN INDEX tables。这一切都是在 3000C DWU

HEAP 与 CCI 的影响

CCI table(更新所有 18 亿行)

-- (staticrc40) 1hr 43m 47s
-- (largerc)   1hr  4m 11s 

HEAP table(更新所有 18 亿行)

-- (staticrc40) 2hr 13m 5s
-- (largerc)        48m 47s

CTAS 整个 table 输出只是切换列值

CTAS 是微软对一切的回答

CCI -- (staticrc40) 56m

堆 -- (staticrc40) 1 小时 35 米

是的,它比更新整个 table 快,但对我们来说还是太慢了

资源的影响class

CCI table 更新 1400 万行 18 亿(smallrc 典型用户)

-- (staticrc40)  1m 30s
-- (smallrc)     1m 40s

CCI table 在将 0 翻转为 1

之前,ROW_SENT_TO_EDW 列上有新的 STATS
-- (staticrc40) 1m 6s
-- (smallrc)    1m 8s

HEAP table 更新 1400 万行 18 亿行(smallrc 典型用户)

-- (staticrc40)  30s
-- (smallrc)     37s

HEAP table 在 ROW_SENT_TO_EDW 列上有新的 STATS,然后将 0 翻转为 1

-- (staticrc40) 25s
-- (smallrc)    25s

因此,在这个特定实例和资源中,HEAPS 的性能似乎优于 CCI classes 确实有所不同,但没有您想象的那么大(存储操作和事务日志记录显然是关键因素)

鬼鬼祟祟完整table更新

最后,完整的 table 变化显然是巨大的并且不是最佳的。所以我们决定考虑完全删除该列,然后使用默认

重新添加它

您必须先找到所有统计数据并将其删除,然后才能删除该列 删除列在 CCI 上需要 15 秒,在 HEAP 上需要 20 秒

在 CCI 上使用默认值将其重新添加需要 29 秒

在 HEAP 上需要 20 秒(对此感到震惊,因为我预计页面写入会花费大量时间,但它显然在幕后做了一些聪明的事情)

您必须立即删除默认约束,否则您将无法再次删除该列并重新打开统计信息。 (这通常需要 20 秒)

但是对于两种类型的 tables,与完整的 table 更新

相比,此方法超级超级快

SQL 对于此删除和添加看起来像这样

-- get the name of the stat to drop
SELECT
              t.[name]                            AS [table_name]
              , s.[name]                          AS [table_schema_name]
              , c.[name]                          AS [column_name]
              , c.[column_id]                     AS [column_id]
              , t.[object_id]                     AS [object_id]
              ,st.[name]    As stats_name
              , ROW_NUMBER()
                     OVER(ORDER BY (SELECT NULL))    AS [seq_nmbr]
       FROM
              sys.tables t
              JOIN sys.schemas s ON  t.[schema_id] = s.[schema_id]
              JOIN sys.columns c ON  t.[object_id] = c.[object_id]
              INNER JOIN sys.stats_columns l ON  l.[object_id] = c.[object_id]
                     AND l.[column_id] = c.[column_id]
                     AND l.[stats_column_id] = 1
              INNER JOIN sys.stats st
                     ON  t.[object_id] = st.[object_id]
                     and l.stats_id = st.stats_id
WHERE t.[object_id] = OBJECT_ID('BKP.SRC_xxx')

DROP STATISTICS BKP.SRC_xxx._WA_Sys_0000000E_2CEA8251

-- now alter the actual table
ALTER TABLE BKP.SRC_xxx 
DROP COLUMN ROW_SENT_TO_EDW
-- 13s

ALTER TABLE BKP.SRC_xxx 
       ADD ROW_SENT_TO_EDW INT NOT NULL DEFAULT 1
-- 1s  


-- find the constraint
SELECT t.[name] AS Table_Name, 
              c.[name] AS Column_Name,
              dc.[name] as DefaultConstraintName
FROM   sys.tables t 
                     INNER JOIN sys.columns c 
                           ON t.[object_id] = c.[object_id] 
                     LEFT OUTER JOIN sys.default_constraints dc
                           ON  t.[object_id] = dc.parent_object_id
                           AND c.column_id = dc.parent_column_id
WHERE  t.[object_id] = OBJECT_ID('BKP.SRC_xxx')
       AND c.[name] = 'ROW_SENT_TO_EDW'

-- drop the constraint
ALTER TABLE BKP.SRC_xxx DROP CONSTRAINT [Cnstr_7183c5bec657448da3475af85110123a]
--  18s

-- create stats
CREATE STATISTICS [thingy] ON BKP.SRC_xxx([ROW_SENT_TO_EDW])
--  9s

我使用了具有 BIT 列的相同模式来识别应该触发维度更新的 changed/inserted ODS 记录。

我没有专门在源表上测试 CCI,我使用了堆,因为它们都少了 10M 行。但是,我确实发现在 BIT 列上有一个非聚集索引并使用 CONVERT(BIT... 这样索引就可以使用了。0 或 1 在没有 [=19= 的 where 子句中] 作为 INT 出现,可以阻止使用 NCI 的能力。这对基于标志列更新 dims 和更新操作的性能产生了巨大影响。

更新src_table 设置 processed_flag = 1 其中 processed_flag = CONVERT(BIT, 0)

顺便说一句 - 完全同意这样一种观点,即 CTAS 往往是解决所有问题的答案,但它并不总是实用,而且通常只是由于必须复制如此多的数据而导致操作速度较慢。