列存储索引-第一部分:架构【中】

原文链接:传送门

这篇文章我们主要分析下列存储索引之所以高效快速的原因。

压缩

通过存储按列分组的数据,就如同值可以被分组因而非常高效的压缩一样。这种压缩常常会将表的大小减小10倍左右,并比标准的SQL Server压缩提供充分的优化。

举个例子,如果一个具有数十亿行的数据表具有一个ID列,并且这个ID列具有100个去重的值,因此平均来说,其每个值都会重复1000万次。压缩相同值的序列是非常容易的并且会导致极小的存储足迹。

就像是标准的压缩一样,当列存储数据被读入内存中时,它仍然是压缩的。它是不会被解压缩的,直到运行时需要。结果就是进行分析查询时会需要更少的内存。这允许更多的数据一次性进行内存。并且,在内存中执行的操作越多,那么查询执行的速度便越快。

压缩是列存储索引之所以快速的首要原因。很小的存储及内存足迹允许大量的数据被读入并保持在内存中以供分析。

考虑下我们在上面建立的表的大小:

305M存储了23,000,000行数据是令人赞叹的。Fact.Order包含了1/100的数据,但却消耗了55M。

 乘以100并且除以列存储索引的大小,我们能近似得到压缩比率为18:这部分是由于列存储索引达到的高效的压缩,也是由于没有必要覆盖额外的非聚集索引。

 内联元数据

作为压缩的一部分,关于底层数据的元数据被收集起来。这允许例如数据行,最小值,最大值,以及数据大小与压缩数据一切内联存储。其结果便是快速生成聚合值的能力,因为我们再也不需要扫描详细数据来生成这些计算值了。

举个例子,在一个OLTP表的最简单的COUNT(*)计算中,我们也需要一个聚集索引扫描,即使没有列数据被返回。如下的查询从我们的新表中返回了表的总数:

SELECT
        COUNT(*FROM dbo.fact_order_BIG_CCI;

它运行的异常快,仅需要116次逻辑读。请注意详细的查询计划:

 传统来说,我们被培训会首先想到的是聚集索引扫描,然而,有了列存储索引之后,这将是我们典型使用的操作符。对于压缩后的OLAP数据,扫描一个大范围的数据是经常发生的并且是正常的。列存储索引已经对这种行为进行了优化并且不用担心比我们现在的数据更多的数据。

我们来看一看STATISTICS IO,我们可以验证新索引使用了极少的IO。

SQL Server可以使用索引中的元数据来满足COUNT函数,而不会统计索引中的每一行数据。如下是OLTP表的IO:

49229次读对比116次读是一个巨大的差异。后续我们会包含更高级的查询和概念。一般来说,使用columnstore索引,对大量行进行操作的聚合查询的执行效率要高得多。

批量执行

当列存储索引被引入的时候,微软也首次亮相了批量模式处理。这是一种查询执行的方法。使用这种方法,数据行被分组处理,而不是在一个单独的线程中一行一行的处理。在SQL Server 2016之前,这需要服务器的最大并行数设置为一个非1的值。这样使用并行处理的查询才可以使用并行模式的优点。从SQL Server 2016开始,SQL Server为并行模式增加了作为一个序列处理的能力,使用一个单独的CPU而不是多个来处理批量的数据行。并行仍可以应用于一个查询,而我们不需要使用并行模式了。

其结果是性能得到了极大的提升,这是因为SQL Server可以将数据批量的读入内存,而不必在执行计划的各个操作符之间来回的传递控制权。对于任何执行计划操作符来说,可以通过检查其属性来验证其是否使用了批量模式。

 对于任何涉及大量数据的分析查询来说,我们都希望使用批量模式。基于如下几点,查询优化器会正确的选择批量模式或者行模式:

  • 表模式
  • 查询架构
  • 服务器设置

如果对列存储索引的查询执行得不好,并且使用了行模式,则有必要进行调查以确定原因。或许是没有足够的CPUs可用,又或许是最大并行数设置为1(在SQL Server 2016 之前)。否则,检查下数据库的兼容性级别,确保其被设置得足够高以利用在服务器的SQL Server版本可用的批量模式的优点。

为了加快将大量数据快速装载到内存中,在可能的情况下,SQL Server会使用大量加载进程以填充行组。这使得数据通过最低限度被记录的过程被移动到列存储索引中。

对于一个报告数据库来说,这是理想的,因为记录每一个插入的行是要耗费时间的并且很可能在一个OLAP环境中是没有必要的。当加载数据到列存储索引中时,我们没有必要来指定大量加载进程(bulk load process)。

【译者注】如上我们列举处理列存储索引之所以快的原因,下一篇我们将谈谈列存储索引的架构。

原文地址:https://www.cnblogs.com/qianxingmu/p/13499266.html