表更新非常慢
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 往往是解决所有问题的答案,但它并不总是实用,而且通常只是由于必须复制如此多的数据而导致操作速度较慢。
我们正在 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 往往是解决所有问题的答案,但它并不总是实用,而且通常只是由于必须复制如此多的数据而导致操作速度较慢。