我已经将几个大表(每个具有 >10^9 行和几十列)从聚集行存储移动到 SQL Server 2014 实例上的聚集列存储索引,并注意到这些的统计信息更新(默认采样,在我们的 ETL 中触发或来自 Hallengren 脚本)现在需要更长的时间。
一个更理论的问题是为什么会这样?我的疯狂猜测是统计信息更新会产生大量随机读取,这与列存储索引不兼容,因为它们更适合大量数据的顺序读取。我很高兴知道更“深入”的解释。
更重要的问题是我是否可以针对它做点什么。我已经在 SQL Server 2017 实例上尝试了针对具有单个 bigint 列(见下文)的表的测试用例,结果相同。增量统计似乎是一个很好的解决方案。我需要重新创建所有统计对象(目前不是增量的,可能是由于历史原因),扩展 ETL 逻辑并更新我们的 Hallengren 脚本版本(我们目前使用旧版本)。如果有人在我进入这个兔子洞之前分享他/她的经验,我将不胜感激。
重现步骤:
/*Create a rowstore and a columnstore table with a single bigint column*/
CREATE TABLE dbo.rowstore (col1 BIGINT);
GO
CREATE TABLE dbo.columnstore (col1 BIGINT);
GO
CREATE CLUSTERED COLUMNSTORE INDEX CCI_columnstore ON dbo.columnstore;
GO
/*Fill both tables with 400 * 10^6 rows. This results in a 15GB large rowstore and a 3,1GB large columnstore tables*/
;WITH e1(n) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), -- 10
e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10
e3(n) AS (SELECT 1 FROM e2 CROSS JOIN e2 AS b), -- 100*100
e4(n) AS (SELECT 1 FROM e3 CROSS JOIN e3 AS b) -- 10000*10000
INSERT dbo.rowstore WITH (TABLOCK)
SELECT CAST(CAST(NEWID() AS VARBINARY(8)) AS BIGINT) FROM e4;
GO 4
INSERT dbo.columnstore WITH (TABLOCK)
SELECT * FROM dbo.rowstore
GO
/*Trigger stats creation*/
SELECT TOP 1 * FROM dbo.rowstore WHERE col1>0
SELECT TOP 1 * FROM dbo.columnstore WHERE col1>0
GO
SET STATISTICS TIME, IO ON
/*This runs 1,5 seconds*/
UPDATE STATISTICS dbo.rowstore
/*This runs 8 seconds and becomes much slower than rowstore on really large tables*/
UPDATE STATISTICS dbo.columnstore
您允许 SQL Server 选择统计数据的采样率。
使用两者的特定样本再次运行测试,您应该会看到更多可比较的时间。
或者更好
由于列存储压缩,很难估计样本百分比中的行数。
在列存储上还有一些工作要做,以解压缩数据并将列组合成行。由于压缩优势和批处理模式处理,这通常可以弥补,但 DDL 计划尚不支持批处理模式。
增量统计是您可以测试的东西,看看它们是否适合您。他们是否获胜取决于您的优先事项。获得正确的初始样本大小可能很困难,并且优化器当前没有利用每个分区的统计信息。如果您经常更新统计数据,并且花费的时间是您最关心的问题,那么这可能是正确的举措。
尽管引用的错误已得到修复,但您可能对在增量更新后统计消失的优秀答案中给出的一般观察感兴趣。