【问题标题】:Why is columnstore index not being used为什么不使用列存储索引
【发布时间】:2017-10-10 10:18:54
【问题描述】:

我在 SQL Server 2016 Enterprise Edition 上的所有列上都有一个 40m 记录的非内存优化表的非聚集列存储索引。

强制使用列存储索引的查询将执行得更快,但优化器会继续选择使用聚集索引和其他非聚集索引。我有很多可用的 RAM,并且正在对维度模型使用适当的查询。

为什么优化器不选择 columnstoreindex?以及如何鼓励使用它(不使用提示)?

这是一个不使用列存储的示例查询:

SELECT
  COUNT(*),
  SUM(TradeTurnover),
  SUM(TradeVolume)
FROM DWH.FactEquityTrade e
--with (INDEX(FactEquityTradeNonClusteredColumnStoreIndex))
JOIN DWH.DimDate d
  ON e.TradeDateId = d.DateId
 JOIN DWH.DimInstrument i
  ON i.instrumentid = e.instrumentid
WHERE d.DateId >= 20160201
AND i.instrumentid = 2

没有提示需要 7 秒,有提示则需要几分之一秒。 没有提示的查询计划是here。 带有提示的查询计划是here

列存储索引的创建语句为:

CREATE NONCLUSTERED COLUMNSTORE INDEX [FactEquityTradeNonClusteredColumnStoreIndex] ON [DWH].[FactEquityTrade]
(
    [EquityTradeID],
    [InstrumentID],
    [TradingSysTransNo],
    [TradeDateID],
    [TradeTimeID],
    [TradeTimestamp],
    [UTCTradeTimeStamp],
    [PublishDateID],
    [PublishTimeID],
    [PublishedDateTime],
    [UTCPublishedDateTime],
    [DelayedTradeYN],
    [EquityTradeJunkID],
    [BrokerID],
    [TraderID],
    [CurrencyID],
    [TradePrice],
    [BidPrice],
    [OfferPrice],
    [TradeVolume],
    [TradeTurnover],
    [TradeModificationTypeID],
    [InColumnStore],
    [TradeFileID],
    [BatchID],
    [CancelBatchID]
)
WHERE ([InColumnStore]=(1))
WITH (DROP_EXISTING = OFF, COMPRESSION_DELAY = 0) ON [PRIMARY]
GO

更新。 Plan using Count(EquityTradeID) instead of Count(*)hint included

【问题讨论】:

  • 您应该提供实际的执行计划和/或 DDL 以获得有意义的答案。
  • 添加查询+计划
  • 以后尝试使用粘贴计划或类似的东西。使人们更快/更容易看到它。 brentozar.com/pastetheplan/?id=S1cM-JfxW
  • 请提供索引 DDL 语句...
  • @Rory 过滤索引?那么优化器就无法知道它是否可以使用索引,它必须首先查询底层数据。如果将InColumnStore =1 添加到查询中会发生什么?

标签: sql-server indexing query-performance sql-server-2016 columnstore


【解决方案1】:

您要求 SQL Server 选择一个复杂的查询计划而不是一个简单的查询计划。请注意,使用提示时,SQL Server 必须将列存储索引与行存储非聚集索引 (IX_FactEquiteTradeInColumnStore) 连接起来。仅使用行存储索引时,它可以进行查找(我假设 TradeDateId 是该索引的前导列)。它确实仍然需要进行密钥查找,但它更简单。

我可以看到两个选项来获得此行为而无需提示:

首先,从列存储索引定义中删除InColumnStore 并覆盖整个表。这就是您对列存储的要求 - 涵盖所有内容。

如果这不可行,您可以使用UNION ALL 显式拆分数据:

WITH workaround
     AS (
         SELECT TradeDateId
              , instrumentid
              , TradeTurnover
              , TradeVolume
         FROM DWH.FactEquityTrade
         WHERE InColumnStore = 1
         UNION ALL
         SELECT TradeDateId
              , instrumentid
              , TradeTurnover
              , TradeVolume
         FROM DWH.FactEquityTrade
         WHERE InColumnStore = 0 -- Assuming this is a non-nullable BIT
        )
     SELECT COUNT(*)
          , SUM(TradeTurnover)
          , SUM(TradeVolume)
     FROM workaround e
          JOIN DWH.DimDate d
            ON e.TradeDateId = d.DateId
          JOIN DWH.DimInstrument i
            ON i.instrumentid = e.instrumentid
     WHERE d.DateId >= 20160201
           AND i.instrumentid = 2;

【讨论】:

  • 您的工会声明已经回答了问题的第二部分。但是对于第一部分,优化器不是像上面评论者所说的那样基于成本吗?
  • 一个基于成本的模型。但是,它只是一个模型。它认为,当它只能读取非聚集索引时,使用列存储索引和行存储非聚集索引的工作量太大。在 SSMS 中,将查询的两个版本(即原始查询和原始 + 索引提示)放在同一个窗口中,并获得估计的执行计划。您会看到有提示的估计成本更高。
【解决方案2】:

您的索引是一个过滤索引(它有一个WHERE 谓词)。

仅当查询的 WHERE 与索引的 WHERE 匹配时,优化器才会使用此类索引。这适用于经典索引,并且很可能适用于列存储索引。当优化器不使用过滤索引时,可能还有其他限制。

因此,要么将WHERE ([InColumnStore]=(1)) 添加到您的查询中,要么将其从索引定义中删除。

您在 cmets 中说过:“InColumnStore 过滤器在加载数据时是为了提高效率。到目前为止,对于所有测试,过滤器覆盖了所有行的 100%”。这里的“所有行”是指“整个表的所有行”还是只是“结果集的所有行”?无论如何,优化器很可能不知道这一点(即使它可以从统计中得出),这意味着使用此类索引的计划必须明确地进行额外的检查/查找,优化器认为这太昂贵了。

这里有几篇关于这个主题的文章:

Why isn’t my filtered index being used? 来自 罗伯·法利

Optimizer Limitations with Filtered Indexes 保罗·怀特。

An Unexpected Side-Effect of Adding a Filtered Index 保罗·怀特。

How filtered indexes could be a more powerful feature by Aaron Bertrand,请参阅优化器限制部分。

【讨论】:

  • "只有当查询的 WHERE 与索引的 WHERE 匹配时,优化器才会使用这样的索引"。这不适用于过滤的列存储索引。我知道,因为我的查询的某些公式将触发列存储索引的使用,而无需提及 InColumnStore 字段。 (例如,使用 cte)
【解决方案3】:

试试这个: 桥接您的查询

Select * 
Into #DimDate
From DWH.DimDate
WHERE DateId >= 20160201

Select  COUNT(1), SUM(TradeTurnover), SUM(TradeVolume)
From DWH.FactEquityTrade e
Inner Join DWH.DimInstrument i ON i.instrumentid = e.instrumentid 
     And i.instrumentid = 2
Left Join #DimDate d ON e.TradeDateId = d.DateId

这个查询的运行速度有多快?

【讨论】:

    猜你喜欢
    • 2015-04-25
    • 1970-01-01
    • 1970-01-01
    • 2014-02-06
    • 1970-01-01
    • 1970-01-01
    • 2017-06-19
    • 2014-08-19
    • 1970-01-01
    相关资源
    最近更新 更多