【问题标题】:How to optimize SQL Server Columnstore Alignment如何优化 SQL Server 列存储对齐
【发布时间】:2018-01-13 03:28:28
【问题描述】:

我有一个用于 IOT 指标(时间序列数据)的聚集列存储索引表。它包含超过 10 亿行,结构如下:

CREATE TABLE [dbo].[Data](
[DeviceId] [bigint] NOT NULL,
[MetricId] [smallint] NOT NULL,
[TimeStamp] [datetime2](2) NOT NULL,
[Value] [real] NOT NULL
)

CREATE CLUSTERED INDEX [PK_Data] ON [dbo].[Data] ([TimeStamp],[DeviceId],[MetricId]) --WITH (DROP_EXISTING = ON)
CREATE CLUSTERED COLUMNSTORE INDEX [PK_Data] ON [dbo].[Data] WITH (DROP_EXISTING = ON, MAXDOP = 1, DATA_COMPRESSION = COLUMNSTORE_ARCHIVE)

从 2008 年到现在,有大约 10,000 个不同的 DeviceId 值和时间戳。针对此表的典型查询如下所示:

SET STATISTICS TIME, IO ON
SELECT
    [DeviceId]
    ,[MetricId]
    ,DATEADD(hh, DATEDIFF(day, '2005-01-01', [TimeStamp]), '2005-01-01') As [Date]
    ,MIN([Value]) as [Min]
    ,MAX([Value]) as [Max]
    ,AVG([Value]) as [Avg]
    ,SUM([Value]) as [Sum]
    ,COUNT([Value]) as [Count]
FROM
    [dbo].[Data]
WHERE
    [DeviceId] = 6077129891325167032
    AND [MetricId] = 1000
    AND [TimeStamp] BETWEEN '2017-07-01' AND '2017-07-30'
GROUP BY
    [DeviceId]
    ,[MetricId]
    ,DATEDIFF(day, '2005-01-01', [TimeStamp])
ORDER BY
    [DeviceId]
    ,[MetricId]
    ,DATEDIFF(day, '2005-01-01', [TimeStamp])

当我执行此查询时,我会得到以下性能指标:

因为目前我认为上述查询的段读取次数过多:

Table 'Data'. Scan count 2, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 5257, lob physical reads 9, lob read-ahead reads 4000.
Table 'Data'. Segment reads 11, segment skipped 764.

查询计划:

我认为这没有得到很好的优化,因为读取了 11 个段以仅检索 10 亿个源行中的 212 个(在分组/聚合之前)

然后我运行 Niko Neugebauer 的出色脚本来验证我们的设置和列存储对齐 https://github.com/NikoNeugebauer/CISL/blob/master/Azure/alignment.sql,我在重建列存储聚集索引后得到了这个结果:

MetricId 和 TimeStamp 列的最佳对齐分数为 100%。我们如何确保 DeviceId 列也很好地对齐?我在初始聚集(行存储)索引中使用了列顺序,这是可以优化的地方吗?

【问题讨论】:

  • 请将查询计划也粘贴为 xml
  • 如果您使用的是 sql server 2016,请尝试使用 dbcc clonedb 并共享数据库,以便其他人可以重现您所面临的确切场景。如果您不使用 2016,您可以编写我们的表格架构、索引、统计信息并尝试共享脚本
  • @TheGameiswar DBCC CLONEDATABASE 从 SQL Server 2014 SP2 开始可用:)
  • @wBob:great ,谢谢你的信息
  • 创建缺失的索引怎么样?

标签: sql-server time-series azure-sql-database columnstore


【解决方案1】:

按 DeviceId 对齐表的关键解决方案是在表上构建聚集行存储索引,然后在其上构建 MAXDOP = 1 的聚集列存储索引(为了不引入索引构建运行时发生的任何重叠多核)。 所以可能的代码看起来像这样:

CREATE CLUSTERED INDEX [PK_Data] ON [dbo].[Data] ([DeviceId],[TimeStamp],[MetricId]) --WITH (DROP_EXISTING = ON)
CREATE CLUSTERED COLUMNSTORE INDEX [PK_Data] ON [dbo].[Data] WITH (DROP_EXISTING = ON, MAXDOP = 1, DATA_COMPRESSION = COLUMNSTORE_ARCHIVE)

另一种可能性是通过准备然后执行对齐功能在 CISL 中完成所有操作:

insert into dbo.cstore_Clustering( TableName, Partition, ColumnName )
    VALUES ('[dbo].[Data]', 1, 'DeviceId' );

这虽然只是针对 1 个分区,但是一旦你知道你正在使用的数字,你还是应该考虑对你的表进行分区。 设置好之后就可以开始执行dbo.cstore_doAlignment,它会自动重新对齐并优化你的表。 (如果您愿意,您将有一些参数来配置优化的阈值)

最好的问候, 尼可

【讨论】:

  • 谢谢@Niko。那么我是否正确理解您的答案,对于我们的用例,我们有一个“仅附加”/记录我们的 IOT 指标的表,我们应该按时间戳列(每月?)对表进行分区,而不是按 DeviceId 对齐这个表?在我们的聚合中,我们总是查询一个 DeviceId、可能的多个不同 MetricId 和一个 1 个月或 12 个月的时间戳(分别按小时和天分组)。
  • Ted,没有所有的小细节(这可能会改变我的看法),我同意它看起来像按 TimeStamp 分区和按 DeviceId 对齐看起来是一个不错的选择。最好的问候,尼科
  • 再次感谢您,@Niko!我已经在 TimeStamp 上实现了每月分区,并按 DeviceId 对齐。结果很有希望,但确实需要一些额外的验证,因为大多数 (75%) 查询在 < 中运行。 100 毫秒,但 25% 需要 2,000 毫秒。中间没多少。它全部在高级 Azure SQL 弹性池上运行。所以这是相同的查询,每 3 秒执行一次,所以没有达到节流裕度。您对分区部分行卷有什么建议?我现在选择月份,但可能会导致分区数量过多?
  • 感谢@NikoNeugebuer 的存在! :) 你的博客太棒了!我有一个与上面提到的类似的场景,当我尝试在 DeviceID 上应用段对齐,并且我的表在 TimeStamp 上被分区为月份时,该列仍然未对齐。它在时间戳上对齐。 DeviceID 0% 对齐。 (在 Azure SQL 上)在 MonthlyPartScheme (TimeStamp) 上的 (DeviceID) 上创建索引。如何在分区内应用段对齐?
【解决方案2】:

当创建聚集列存储的 Max dop 设置为 1 时,将有效地对记录进行排序,但对于具有 10 亿行的表,此 max dop 1 将无济于事。最好用任何日期列对表进行分区,然后创建一个最大 dop 大于 1 或 0 的聚集列存储索引。但在这种情况下,不能保证排序,但聚集列存储索引会执行该段有效消除。应该明确指出,不要通过在表中保留任何其他非聚集索引来删除或创建聚集列存储索引,这将影响你的聚集列存储索引创建/删除性能。如果您通过保留其他索引来删除聚集列存储索引,SQL 服务器将对其他索引执行大量工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-06
    • 1970-01-01
    • 1970-01-01
    • 2012-01-08
    • 2013-07-31
    • 1970-01-01
    相关资源
    最近更新 更多