聚类索引新基数运算符灾难性估计

如何解决聚类索引新基数运算符灾难性估计

最近一段时间后,我们将sql Server的兼容性级别从110(sql server 2012)更改为150(2019),

我们正在观察一个查询,它变得非常慢并且超时。该查询涉及以下2个表:

CREATE TABLE [Workflow].[Transaction](
    [TransactionId] [uniqueidentifier] NOT NULL,[StartDate] [datetimeoffset](7) NOT NULL)

CREATE TABLE [Workflow].[TransactionData](
    [Id] [bigint] IDENTITY(1,1) NOT NULL,[TransactionId] [uniqueidentifier] NOT NULL,[Date] [datetimeoffset](7) NOT NULL,[StateId] [int] NOT NULL)

每个订单交易记录都保存在[Workflow].[Transaction]的100M行中,这些行是交易通过的状态,例如“开始”,“进行中”,“成功”,“失败”保存在[Workflow].[TransactionData] 200M行中。

[Workflow]。[Transaction] .StartDate上的Clusterdindex,大多数情况下,查询是根据时间范围完成的。 TransactionId上的非聚集索引。 TransactionData在TransactionId中也有一个非集群索引,其中StateId和Date为包含列,

我们的查询想要获取每个交易的最后状态:

SELECT
        T.TransactionId,LastState.StateId,T.StartDate
    FROM Workflow.[Transaction] T
    CROSS APPLY (
        SELECT 
            TOP 1 
            StateId 
        FROM Workflow.[TransactionData] td
        WHERE td.TransactionId = t.TransactionId
        ORDER BY [Date] DESC,StateId DESC
    ) LastState
    WHERE   
            @startDate <= t.StartDate 
            AND t.StartDate < @endDate
    ORDER BY T.StartDate asc

使用兼容性级别150,并且基本上使用2014年发布的新基数运算符,由于估算非常错误,因此查询花费了很长时间,因此计划很糟糕。索引假脱机190M行。您可以看到完整的计划here

enter image description here

如果我们使用传统基数运算符,请在提示添加选择的结尾:

OPTION (USE HINT ('FORCE_LEGACY_CARDINALITY_ESTIMATION'))

我们得到了更好的估算和计划。您可以看到完整的计划here

enter image description here

看到这一点,我们检查了两个表的统计信息,它们都在1天之内更新了。

运行简单的select时,问题更加明显

 SELECT
        TransactionId
    FROM Workflow.[Transaction]
    where StartDate >= '2020-11-05 00:00:00 +02:00' and 
          StartDate < '2020-11-06 00:00:00 +02:00'

enter image description here

估计:7791,实际108855。查询计划可见here。 如果我们再次通过HINT启用传统基数运算符,则会得到更好的估计:131315(82%)。可以看到新的查询计划here

集群索引统计的要点:

                Histogram Steps
                    RANGE_HI_KEY                      RANGE_ROWS                         EQ_ROWS             disTINCT_RANGE_ROWS                  AVG_RANGE_ROWS
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    10/27/2020 2:58:32 AM +02:00                        731904.2                               1                          727784                        1.005661
   10/31/2020 12:14:20 PM +02:00                          470738                               1                          468159                        1.005508
     11/6/2020 3:40:41 PM +02:00                        805856.3                               1                          802208                        1.004547

为什么新的基数运算符会对看起来很琐碎的情况做出如此糟糕的估计?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?