列存储段消除(ColumnStore Segment Elimination)

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
云数据库 RDS SQL Server,基础系列 2核4GB
简介:

列存储索引是好的!对于数据仓库和报表工作量,它们是真正的性能加速器。与聚集列存储结合,你会在常规行存储索引(聚集索引,非聚集索引)上获得巨大的压缩好处。而且创建聚集列存储索引非常简单:

CREATE CLUSTERED COLUMNSTORE INDEX ccsi ON TableName
GO

但这是你对聚集列存储需要知道的一切?并不是,如你在这篇文章会看到的……

什么是列存储段(ColumnStore Segments)?

在我各个研讨会和公共培训课程期间,我经常开玩笑:一旦你开释使用聚集列存储索引,你就不需要知道索引的更多信息。使用聚集列存储索引很太多的优点,它会带来巨大的性能提升:

  • 更好的压缩
  • 批处理模式执行
  • 更少I/O,更好内存管理
  • 段消除

如你从下例子看到的,在SQL Server里创建聚集列存储索引非常简单:

CREATE CLUSTERED COLUMNSTORE INDEX idx_ci ON FactOnlineSales
GO

你只需指定表名,没别的。甚至你不需要担心聚集键列,因为这个概念对列存储索引不适用。很简单,是不是?让我们在适当的地方用刚才的聚集索引运行一个简单的查询:

复制代码
-- Segment Elimination doesn't work quite well, because
-- we have a lot of overlapping Segments.
SELECT
    DateKey, 
    SUM(SalesAmount) 
FROM FactOnlineSales_Temp
WHERE
    DateKey >= '20090101' 
    AND DateKey <= '20090131'
GROUP BY
    DateKey
GO
复制代码

这个查询非常快,因为对于查询执行,SQL Server可以使用聚集列存储索引。从STATISTICS IO输出也向你展示了,对于聚集列存储索引不需要很多LOB Logical Reads

但那些段读取(Segment Read)和段跳过(Segment Skipped)度量呢?

你们也许知道列存储索引内部分成所谓的列存储段(ColumnStore Segments)。一个列存储段通常指定到特定的列和行组。一个行组包含近100万行。下图很好的展示了这个重要概念:

来源:https://www.microsoft.com/en-us/research/publication/enhancements-to-sql-server-column-stores/

什么是列存储段消除(ColumnStore Segment Elimination)?

这里最重要的是,对于每个列存储段,SQL Server内部存储了最小和最大的值。基于这些值,SQL Server可以进行所谓的段消除。段消除意味着SQL Server只读取包含请求数据的那些段(在访问列存储索引时)。你可以认为它是和分区消除一样得方式,在你和分区表打交道的时候。但这里的消除发生在列存储段级别。

如你在刚才的图片所见,在列存储索引访问期间SQL Server不能消除任何段,因为默认情况下,在列存储索引里你没有排列顺序。你数据的排列顺序取决于在执行计划里,在你创建列存储索引时,SQL Server如何读取数据:

 

如你所见,聚集列存储索引通过从最初包含数据的堆表创建。因此在聚集列存储索引里,你没有排列顺序,因此段消除不能很好为你工作。

如何改善情况?在你的数据里首先通过创建传统的行存储聚集索引来强制排序,然后修改它为聚集列存储索引!偶滴神啊……

复制代码
-- Now we create a traditional RowStore Clustered Index to sort our
-- table data by the column "DateKey".
CREATE CLUSTERED INDEX idx_ci ON FactOnlineSales_Temp(DateKey)
GO

-- "Swap" the Clustered Index through a Clustered ColumnStore Index
CREATE CLUSTERED COLUMNSTORE INDEX idx_ci ON FactOnlineSales_Temp
WITH (DROP_EXISTING = ON)
GO
复制代码

有了传统的聚集行存储索引就位,当你创建聚集列存储索引时,在执行计划里,查询优化器会引用这个索引:

作为副作用,在聚集列存储索引里,你现在应该有已排序的数据,段消除应该会很好处理:

复制代码
-- Segment Elimination works better than previously, but still not perfectly.
SELECT
    DateKey, 
    SUM(SalesAmount) 
FROM FactOnlineSales_Temp
WHERE
    DateKey >= '20090101' 
    AND DateKey <= '20090131'
GROUP BY
    DateKey
GO
复制代码

但当你再次查看STATISTICS IO的输出,SQL Server还是需要读取很多段,只跳过其中几个:

但为什么SQL Server不能跳过所有的段而只跳过几个?问题存在于聚集列存储的创建。当你回头看刚才的执行计划,你会看到ColumnStore Index Insert (Clustered) 运算符是并行运行的——通过多个工作者线程。而且这些工作者线程再次破坏了聚集列存储索引里你数据的排序!你从聚集行存储索引里进行你的数据读取,然后聚集列索引的并行创建重排了你的数据……伤及无辜~~~

你只能通过使用MAXDOP为1的聚集列存储创建来解决这个问题:

CREATE CLUSTERED COLUMNSTORE INDEX idx_ci ON FactOnlineSales_Temp
WITH (DROP_EXISTING = ON, MAXDOP = 1)
GO

这听起来很糟糕,事实也如此!但这是唯一让你在列存储索引里阻止重排你数据的解决方法。当你接下来从聚集列存储数据读取后,你会看到SQL Server终于能跳过所有的段:

 小结

聚集列存储索引很好——真的很好!但默认段消除不能很好进行,因为在你的聚集列存储里没有预定义的排序。因此在你调优你的列存储查询时,你要确保段消除可以正常进行。而且有时候你甚至需要通过使用MAXDOP 1来阻止你的数据排序……



本文转自Woodytu博客园博客,原文链接:http://www.cnblogs.com/woodytu/p/6376617.html,如需转载请自行联系原作者

相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
相关文章
|
4月前
|
存储 关系型数据库 MySQL
MySQL数据库——InnoDB引擎-逻辑存储结构(表空间、段、区、页、行)
MySQL数据库——InnoDB引擎-逻辑存储结构(表空间、段、区、页、行)
80 7
|
5月前
|
存储 关系型数据库 MySQL
认真学习InnoDB的数据存储结构中的区、段与表空间
认真学习InnoDB的数据存储结构中的区、段与表空间
115 2
|
SQL 关系型数据库 MySQL
mysql使用 from两表查询与join两表查询区别
在mysql中,多表连接查询是很常见的需求,在使用多表查询时,可以from多个表,也可以使用join连接连个表这两种查询有什么区别?哪种查询的效率更高呢? 带着这些疑问,决定动手试试1.先在本地的mysql上先建两个表one和twoone表 CREATE TABLE `one` ( `id`.
5493 0
|
存储 Go 索引
SQLServer中重建聚集索引之后会影响到非聚集索引的索引碎片吗
原文:SQLServer中重建聚集索引之后会影响到非聚集索引的索引碎片吗   本文出处:http://www.cnblogs.com/wy123/p/7650215.html (保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错误进行修正或补充,无...
1176 0
|
Oracle 关系型数据库 索引
oracle优化一则--不走索引
字段类型转换会造成无法使用索引的情况,如果非要进行转换,可以创建带有转换类型的索引,函数索引。
14536 0
|
存储 关系型数据库 MySQL