为啥表上存在主键会显着提高列存储索引的性能?

Posted

技术标签:

【中文标题】为啥表上存在主键会显着提高列存储索引的性能?【英文标题】:Why does the presence of primary key on the table significantly enhance the performance of column-store indexes?为什么表上存在主键会显着提高列存储索引的性能? 【发布时间】:2015-04-03 13:15:24 【问题描述】:

我试图查看列存储索引可以在表上提供什么样的性能提升。该表有大约 370 万行、11 列,并存储为堆(即没有主键)。我在表上创建了一个列存储索引并运行以下查询:

SELECT 
    [Area], [Family],
    AVG([Global Sales Value]) AS [Average GlobalSalesValue],
    COUNT([Projected Sales])
FROM 
    dbo.copy_Global_Previous5FullYearSales
WHERE 
    [Year] > 2012  
GROUP BY 
    [Area], [Family]

创建表语句如下:

CREATE TABLE [dbo].[copy_Global_Previous5FullYearSales]
(
    [SBU] [NVARCHAR](10) NULL,
    [Year] [INT] NULL,
    [Global Sales Value] [MONEY] NULL,
    [Area] [NVARCHAR](50) NULL,
    [Sub Area] [NVARCHAR](50) NULL,
    [Projected Sales] [MONEY] NULL,
    [Family] [NVARCHAR](50) NULL,
    [Sub Family 1] [NVARCHAR](50) NULL,
    [Sub Family 2] [NVARCHAR](50) NULL,
    [Manufacturer] [NVARCHAR](40) NULL,
    [rowguid] [UNIQUEIDENTIFIER] NOT NULL,
    [ID] [INT] IDENTITY(1,1) NOT NULL,

    PRIMARY KEY CLUSTERED ([ID] ASC)
        WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
              IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
              ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

在这种情况下,我从列存储索引获得的性能提升可以忽略不计。使用列存储索引的查询的运行速度几乎与没有索引的原始查询一样慢,在某些情况下甚至更慢,尽管也使用了批处理模式。

令人惊讶的是,当我在现有表上创建一个不断增加的主键 ID 并重建列存储索引时,CPU 时间提高了 15 倍,运行时间提高了 3 倍。

我不明白添加主键如何影响列存储索引的查询性能,列存储索引无论如何都以压缩格式存储数据。此外,主键只会改变页面的顺序,在这种情况下,不会改变。

下面是执行计划

【问题讨论】:

附注:堆表并不意味着没有主键。您可以毫无问题地创建非聚集主键(这实际上对某些类型的主键有意义) @Martin - 嗨,我添加了 create table 语句。计划是完整的,只是单独剪断。所以,在排序之后,有一个流聚合。 @user2673722 谢谢,是的,我意识到了计划中的这一点。堆和 CI 案例的计划是否相同?您是否查看了这两种情况下的读取次数以及两种情况下索引占用的大小? @a_horse_with_no_name - 好吧,我对数据库和 SQL 很陌生,我可能会研究一下。但目前这个表只有主键和唯一标识符 rowguid。 @Martin - 是的,执行计划完全相同。另外,这里是 [Heap/CI] 格式的逻辑读取的摘要 - 80555/14646,物理读取 - 0/1 【参考方案1】:

键的存在会改变列存储的构建方式。因为构建器按顺序获取输入,所以生成的段是段消除的更好候选者。阅读更多Ensuring Your Data is Sorted or Nearly Sorted by Date to Benefit from Date Range Elimination:

数据仓库查询中最常见的过滤器类型是按日期。如果系统可以确定没有行符合条件,列存储段消除可帮助您跳过整个一百万行段,只需查看段中列的最小值和最大值。因此,您通常需要确保您的段按日期排序或几乎排序,以便可以尽快执行日期过滤器。

您的订单是由 ID 订购的,但我很确定这会导致功能依赖的副作用。

【讨论】:

嗨,我按 ID 排序,但我将如何改变页面的排序方式,因为 ID 列只是为每一列添加一个唯一的数字......另外,我什至没有创建列存储索引时使用 ID 列。 @user2673722 如果您仍然拥有表的两个副本,这里有一些查询可以用来查看段边界和段消除sqlskills.com/blogs/joe/… @user2673722 阅读链接的文章,他们解释了为什么拥有 ID 很重要,即使你从不使用它 @RemusRusanu - 从你建议的链接中我真的找不到任何有用的东西。它表明当数据按日期排序时,使用聚集键,那么段消除变得容易;这是可以理解的,但就我而言,我创建了一个名为 ID 的附加列,这并没有加快我认为的任何事情 @Martin - 您提供的链接非常有帮助。如果您可以将其添加为下面的答案,那就太好了,所以我可以指出这是正确的答案

以上是关于为啥表上存在主键会显着提高列存储索引的性能?的主要内容,如果未能解决你的问题,请参考以下文章

您如何计算在大型 Postgresql 表上创建索引的时间?

innodb 存储引擎为啥要用一个自增的主键

向索引列添加外键会提高性能吗?

在现有的大型表上创建列存储索引的最有效方法?

在具有聚集列存储索引的表上创建触发器 - 错误

在具有 350 亿行的列存储索引表上重新创建索引