列存储索引-第一部分:架构【上】
原文链接:传送门。
微软随着SQL Server 2012的发布引入了列存储索引这一特性,并且从那时候起,在每一个发行版本中都会对其进行改进。你或许想知道为什么它们与传统的索引有所不同以及它们是如何工作的。
在这个系列里,Edward Pollack解释了列存储索引的架构。在本系列接下来的文章中,他会详细描述列存储索引的最佳实践。
在任何关系型数据库中,索引是高性能的关键所在。几乎没有任何索引增强被介绍为其可以提高查询速度以及性能像列存储索引那样令人抓狂。
SQL Server的每个后续版本都引入了对这一特性的改进,并将columnstore索引转换为索引OLAP工作负载的go-to方法。
在这篇文章中,我们将回顾列存储索引的基础,在后续的文章之后,我们将深入最佳实践,并探寻更高级的索引考虑。我个人非常喜欢在大量的程序中使用列存储索引,从小的分解表到大规模的数据仓库,并期望分享我的经验(好的,不好的,以及愚蠢的)。
因为这篇文章通过了对列存储索引的讨论,因而出现的信息都会具有一定的特定性。刚开始我们会讨论其概率及产生的益处,接下来讨论基础的架构,再而会渐渐深入在其之上的细节。
测试数据
在深入列存储索引之前,我们有必要创建一个大表,将其索引化,并且在整篇文章中都使用它。在每章中都有一个可用的示例可以帮助我们解释这些索引是如何工作的以及为何它们如此之快。它同样也允许我们演示了最佳实践以及如何不使用它们。WorldWideImportersDW 示例数据库将被用来生成数据库模式及数据集,以供我们进行使用。如下的T-SQL语句会创建并用足够多的行来填充一个表,以高效的演示列存储索引。
CREATE TABLE dbo.fact_order_BIG_CCI ( [Order Key] [bigint] NOT NULL, [City Key] [int] NOT NULL, [Customer Key] [int] NOT NULL, [Stock Item Key] [int] NOT NULL, [Order Date Key] [date] NOT NULL, [Picked Date Key] [date] NULL, [Salesperson Key] [int] NOT NULL, [Picker Key] [int] NULL, [WWI Order ID] [int] NOT NULL, [WWI Backorder ID] [int] NULL, [Description] [nvarchar](100) NOT NULL, [Package] [nvarchar](50) NOT NULL, [Quantity] [int] NOT NULL, [Unit Price] [decimal](18, 2) NOT NULL, [Tax Rate] [decimal](18, 3) NOT NULL, [Total Excluding Tax] [decimal](18, 2) NOT NULL, [Tax Amount] [decimal](18, 2) NOT NULL, [Total Including Tax] [decimal](18, 2) NOT NULL, [Lineage Key] [int] NOT NULL); INSERT INTO dbo.fact_order_BIG_CCI SELECT [Order Key] + (250000 * ([Day Number] + ([Calendar Month Number] * 31))) AS [Order Key] ,[City Key] ,[Customer Key] ,[Stock Item Key] ,[Order Date Key] ,[Picked Date Key] ,[Salesperson Key] ,[Picker Key] ,[WWI Order ID] ,[WWI Backorder ID] ,[Description] ,[Package] ,[Quantity] ,[Unit Price] ,[Tax Rate] ,[Total Excluding Tax] ,[Tax Amount] ,[Total Including Tax] ,[Lineage Key] FROM Fact.[Order] CROSS JOIN Dimension.Date WHERE Date.Date <= '2013-04-10' CREATE CLUSTERED COLUMNSTORE INDEX CCI_fact_order_BIG_CCI ON dbo.fact_order_BIG_CCI WITH (MAXDOP = 1);
产生的结果表,dbo.fact_order_BIG_CCI包含了 23,141,200 条数据,其提供了足够多的数据来供我们实验并演示列存储索引的性能。对于大部分分析负载来说,聚集列存储索引是我们需要的所有的东西。然而添加非聚集的行存储索引来满足特定的OLTP查询需求可能会很有价值。它们通常是根据具体情况添加的,因为它们将向表中添加额外的存储、内存和处理占用空间。
为了提供一个肩并肩的比较,如下的T-SQL会创建一个额外的测试表,其使用了一个经典的基于BIGINT标识符的聚集索引。我们在[Order Date Key]上添加了一个额外的非聚集索引来促进安装日期的查询:
CREATE TABLE dbo.fact_order_BIG ( [Order Key] [bigint] NOT NULL CONSTRAINT PK_fact_order_BIG PRIMARY KEY CLUSTERED, [City Key] [int] NOT NULL, [Customer Key] [int] NOT NULL, [Stock Item Key] [int] NOT NULL, [Order Date Key] [date] NOT NULL, [Picked Date Key] [date] NULL, [Salesperson Key] [int] NOT NULL, [Picker Key] [int] NULL, [WWI Order ID] [int] NOT NULL, [WWI Backorder ID] [int] NULL, [Description] [nvarchar](100) NOT NULL, [Package] [nvarchar](50) NOT NULL, [Quantity] [int] NOT NULL, [Unit Price] [decimal](18, 2) NOT NULL, [Tax Rate] [decimal](18, 3) NOT NULL, [Total Excluding Tax] [decimal](18, 2) NOT NULL, [Tax Amount] [decimal](18, 2) NOT NULL, [Total Including Tax] [decimal](18, 2) NOT NULL, [Lineage Key] [int] NOT NULL); INSERT INTO dbo.fact_order_BIG SELECT [Order Key] + (250000 * ([Day Number] + ([Calendar Month Number] * 31))) AS [Order Key] ,[City Key] ,[Customer Key] ,[Stock Item Key] ,[Order Date Key] ,[Picked Date Key] ,[Salesperson Key] ,[Picker Key] ,[WWI Order ID] ,[WWI Backorder ID] ,[Description] ,[Package] ,[Quantity] ,[Unit Price] ,[Tax Rate] ,[Total Excluding Tax] ,[Tax Amount] ,[Total Including Tax] ,[Lineage Key] FROM Fact.[Order] CROSS JOIN Dimension.Date WHERE Date.Date <= '2013-04-10'; CREATE NONCLUSTERED INDEX IX_fact_order_BIG ON dbo.fact_order_BIG ([order date key]);
列存储索引:概述
列存储索引使用了我们所知道的关于数据存储和SQL Server查询的许多东西并将其在自己的头脑中进行转换。许多我们曾经遵循的一些惯例在这儿都不适用,包括了开发T-SQL,一些争论以及优化的东西。
非常重要的一点是,我们要注意到列存储索引是为非常巨大的表而建立的。如果一个表没有包含百万或者十亿的数据行,那么其不会完全从列存储索引中收益。一般来说,表越大,列存储索引会对其加速的更快。
列存储索引被设计用来支持SQL Server中的分析。在这个特性之前,具有数十亿行数据的表是一件麻烦的事并且用适当的速度和性能对其进行查询是一件很有挑战性的事。我们常常被迫预先处理许多Fact表来回答我们以后可能会问的问题。这个进程驱使了SQL Server BI好几年。它同样驱使了许多分析从SQL Server中剥离出来并集成进SSAS或者大量的第三方工具中。
随着数据呈指数级增长,有一个强烈的需求,使得我们能够维护详细的Fact数据,可以直接针对报告和分析。列存储索引可以为经典的OLAP查询提供10倍,百倍,甚至更多的速度推进。当它们为OLAP工作负载的性能提供足够的推进时,这些分析开始于聚集的列存储索引。
稍后的章节我们会列举一些列存储索引能获得如此性能的原因。
To be continued...