星型连接查询优化 - 更改分区,使用列存储?

Dee*_*pak 7 sql-server ssis partitioning

我试图了解提高客户给我的查询性能的最佳方法。它包含几个连接的表,其中一个被称为dwh.fac_sale_detail包含 15 亿行的表。

该表dwh.fac_sale_detail根据其名为 的列之一进行分区TradingDateKey1。它实际上以 yyyymmdd 格式存储数据,但它是INTDatatype。

这有从 2005 年到 2015 年的 TradingDateKeys,但分区只创建到 2014 年。

另一个团队中的一个人提出了以下建议,我正在尝试遵循他的建议,但我是创建或更改分区的新手,不知道这是否真的会对查询性能产生任何影响:

他用他自己的话来说是“FactSalesDetail表目前大约有 15 亿行,目前TradingDate按年划分为 10 个分区,每个分区大约有 1.5 亿行。最好将最近一年进一步划分为月分区并在所有分区上应用列存储索引。在每个分区上应用索引将是一次性的,您应该只需要维护当前分区的索引。”

这是我尝试优化的查询的查询计划

另请参阅随附的屏幕截图以更好地理解:

在此处输入图片说明

在此处输入图片说明

Geo*_*son 12

感谢添加查询计划;这是非常有用的信息。我有一些基于查询计划的建议,但首先要注意:不要只接受我所说的并假设它是正确的,首先尝试一下(最好在您的测试环境中)并确保您了解更改的原因或者不要改进您的查询!

查询计划:概述

从这个查询计划(以及相应的 XML)中,我们可以立即看到一些有用的信息:

  • 您使用的是 SQL 2012
  • 这是一个经典的星形连接查询,您将受益于 SQL 2008 中为此类计划添加的行内位图过滤器优化
  • 事实表包含大约 15 亿行,其中超过 5 亿行与维度过滤器匹配
  • 查询请求 72GB 的内存,但只被授予 12GB 的内存(大概 12GB 是授予任何给定查询的最大值,这意味着您的机器可能有大约 64GB 的内存)
  • SQL Server 正在执行一种排序流聚合,它将 5 亿行减少到 600,000 行。排序超出了它的内存授予并溢出到 tempdb
  • 由于查询中的显式和隐式转换,我们对影响计划的转换发出警告
  • 该查询使用 32 个线程,但对事实表的初始查找具有巨大的线程偏差;32 个线程中只有 2 个完成所有工作。(但是,在查询计划的后续步骤中,工作更加平衡。)

在此处输入图片说明

优化:列存储与否

这是一个棘手的问题,但总的来说,在这种情况下,我不会为您推荐列存储。主要原因是您使用的是 SQL 2012,所以如果您能够升级到 SQL 2014,我认为可能值得尝试列存储。

通常,您的查询是专为列存储设计的类型,并且可以从列存储减少的 I/O 和批处理模式更高的 CPU 效率中受益匪浅。

但是,SQL 2012列存储局限性太大了,并且tempdb 溢出行为,其中任何溢出都会导致 SQL Server 完全放弃批处理模式,这可能是一种毁灭性的惩罚,可能会对您处理的大量行产生影响。正在与。如果您确实在 SQL 2012 上使用列存储,请准备好密切关注您的所有查询,并确保始终可以使用批处理模式。

优化:更多分区?

我认为更多的分区不会帮助这个特定的查询。当然,欢迎您尝试,但请记住,分区主要是一种数据管理功能(通过SWITCH PARTITION而不是性能功能在 ETL 过程中交换新数据的能力。它在某些情况下显然可以帮助提高性能,但同样地,它会损害其他方面的性能(例如,许多单例搜索现在每个分区必须执行一次)。

如果您确实使用列存储,我认为加载数据以更好地消除段将比分区更重要;理想情况下,您可能希望每个分区中的行尽可能多,以便拥有完整的列存储段和出色的压缩率。

优化:改进基数估计

因为您有一个巨大的事实表和来自每个维度表的少量非常小的(数百或数千行)行集,所以我建议您使用一种方法明确创建一个临时表,其中仅包含您计划使用的维度行. 例如,您应该编写一个预处理查询以仅提取您关心的行,并将适当的 PK 添加到这些行,而不是Dim_Date使用像 那样的复杂逻辑连接。cast(right(ALHDWH.dwh.Dim_Date.Financial_Year,4) as int) IN ( 2015, 2014, 2013, 2012, 2011 )Dim_Date

这将允许 SQL Server 仅对您实际使用的行创建统计信息,这可能会在整个计划中产生更好的基数估计。由于与整体查询复杂性相比,这种预处理的工作量非常小,因此我强烈推荐此选项。

优化:减少螺纹歪斜

很可能从Dim_Date它自己的表中提取数据并向该表添加主键也有助于减少线程倾斜(跨线程的工作不平衡)。这是一张有助于说明原因的图片:

在此处输入图片说明

在本例中,该Dim_Date表有 22,000 行,SQL Server 估计您将使用其中的 7,700 行,而您实际上只使用了其中的 1,827 行。

因为 SQL Server 使用统计信息来将行范围分配给线程,所以在这种情况下基数估计很差可能是行分布很差的根本原因。

1,872 行上的线程倾斜可能无关紧要,但痛苦的一点是,这会向下级联到 15 亿行事实表中的查找,其中我们有 30 个线程闲置,而 6 亿行正在由 2 个线程处理。

在此处输入图片说明

优化:摆脱排序溢出

我要关注的另一个领域是排序溢出。我认为在这种情况下的主要问题是基数估计不佳。正如我们在下面看到的,SQL Server 认为由 aSort和a 组合执行的分组操作Stream Aggregate将产生 3.24 亿行。然而,它实际上只产生 643,000 行。

在此处输入图片说明

如果 SQL Server 知道从这个分组中出来的行很少,它几乎肯定会使用HASH GROUP(Hash Aggregate) 而不是SORT GROUP(Sort-Stream) 来实现您的GROUP BY子句。

如果您进行上述其他一些更改以改进基数估计,这可能会自行解决。但是,如果没有,您可以尝试使用OPTION (HASH GROUP) 查询提示来强制 SQL Server 这样做。这将让您评估改进的幅度并决定是否在生产中使用查询提示。我通常会对查询提示持谨慎态度,但HASH GROUP与使用连接提示、使用FORCE ORDER或以其他方式从查询优化器手中夺走太多控制权相比,指定只是轻而易举。

优化:内存授予

最后一个潜在问题是 SQL Server 估计查询需要使用 72GB 的内存,但您的服务器无法为查询提供这么多内存。虽然从技术上讲,向服务器添加更多内存会有所帮助,但我认为至少还有其他几种方法可以解决这个问题:

  • 去掉Sort操作符(如上所述);它确实是唯一一个在查询中消耗大量内存的运算符
  • 将您的查询分成多个批次;例如,您可以为每个分区运行一次查询。这可以减少排序的大小,将其保存在内存中,并有可能显着提高性能。一个附带好处可能是,如果您只访问一个分区,您可能会更好地利用线程,因为在某些情况下这确实会影响 SQL Server 将线程分配给分区的方式。