在今天的文章里,我想专门详细谈下SQL Server 2014引入的可更新聚集列存储索引(Updateable Clustered ColumnStore Index)。在我们进入细节讨论前,我想先给你简单介绍下它在SQL Server 2012里出现时的情况,还有它们的局限性。
SQL Server 2012里的列存储索引
SQL Server 2012里引入的列存储索引是最热的新特性之一(除AlwaysOn外)。如果使用得当的话,对于数据仓库的工作负荷会带来巨大的性能提升。遗憾的是,它们有2个大的局限性:
- 只对非聚集列存储索引支持
- 一旦在表上建立了列存储索引,这个表就只读了,对其数据不允许修改
对一些用户来说,这2个局限性是非常糟糕的。假设你有传统行存储数据的300GB表。使用列存储索引是可以将数据压缩到30GB大小。但SQL Server 2012只允许非聚集列存储索引,这样的话你需要2次存储你的数据:一个在传统行存储格式里,另一个在新的列存储格式里。那是存储的巨大浪费,因为你的查询只用到你的非聚集列存储索引。
一旦你创建了你的非聚集列存储索引,你就不允许修改表数据了——你的表就只读了!当然,对这个问题有一些解决方法,例如分区交换,但你还是需要你自己来实现……
SQL Server 2014里的列存储索引
现在SQL Server 2014做出了一些改变,因为微软已经解决了上述问题——用了一些魔法和幻想:SQL Server 2014提供你可更新的聚集列存储索引(Updateable Clustered ColumnStore Index)!我们一起来具体看下在SQL Server里这个魔法和错误内部发生了什么。
第1个最重要的事实是:直接更新列存储索引是不可能的!在你的INSERT,UPDATE和DELETE事务期间进行完全的解压和压缩太费时间了。因此SQL Server 2014从一些魔法里获得帮助:Delta Stores和Delete Bitmap。我们详细看下这2个概念。
每次当你只执行INSERT语句,新记录不直接插入列存储索引——记录会插入Delta Store。Delta Store本身就是典型的B树,有B树的所有缺点和优点。接下来当你从列存储索引读取时,SQL Server同时从压缩的列存储索引和Delta Store里返回你数据。
当你执行DELETE语句时,在压缩的列存储索引里还是什么也没发生。唯一发生的是通过Delete Bitmap记录被逻辑删除。在列存储索引里的每条记录在Delte Bitmap里都有对应位。当你再次读取列存储索引时,SQL Server会忽略在Delete Bitmap里标记为删除的记录。
执行UPDATE语句意味着在Delta Store里插入新的记录,在Delete Bitmap里把原版本记录标记为删除。很简单,是不是?下图显示了详细展示这个概念(来源:http://research.microsoft.com/pubs/193599/Apollo3%20-%20Sigmod%202013%20-%20final.pdf):
因为Delta Store和Delete Bitmap让你的列存储索引看起来是可更新的,但事实上是不可变的。还有个叫做Tuple Mover的后台处理,它定期运行最后把你的修改异步到列存储索引。
在SQL Server 2014里,另外你终于可以定义聚集列存储索引。这就是说你可以不需要将你的数据存储在传统行格式里。直接创建你的表,在上面创建聚集列存储索引。用这个方法可以在存储里节约大量空间,因为现在一切都是压缩的。当你进一步和关系数据库概念打交道时,聚集(Clustered)意味着已排序(Sorted)。但是使用聚集列存储索引这句话是不对的:当你创建了聚集列存储索引时,在你数据里是没有排序的!请意识到这个小区别!
小结
可更新的聚集列存储索引是SQL Server 2014一个神奇的幻想。不要理解错了:我是的确很喜欢用这个新特性引入的发展潜能,但你如果想要充分用好它,就要理解这个特性内部是如何实现的。
感谢关注!