SQL Server 自2012以來引入了列存儲的概念,至今2016對列存儲的支持已經是非常友好了。由於我這邊線上環境主要是2014,所以本文是以2014為基礎的SQL Server 的列存儲的介紹。下面我們主要看一下列存儲的發展以及一些原理:
列存儲的開發是想要處理超大量數據進行分析計算,於是在SQL Server 2012時,SQL Server 引入了列存儲索引,用以顯著提供高傳統數據倉庫類型語句的性能,並在SQL Server 2014中做了進一步加強。列存儲會將一個列的數據單獨存放在一起,所以主要會有以下兩個優點。
1:同一個列中的數據的相似性比較高,因此壓縮比例會更高。磁盤操作時,磁盤的IO也會相應的降低。當然,當壓縮的數據讀取到內存后解壓會需要額外的CPU。
2:由於數據是按照列進行存儲和讀取的,因此如果某些列在訪問中並不需要,那么實際的操作時也會不訪問這些列,那么磁盤IO會進一步降低。
3:由於數據是按照列進行存儲和讀取的,大批量的數據聚合訪問等會較以往的行存儲更快。
對於列存儲來說,主要來說就是數據倉庫這個使用場景了,微軟最近幾年也是在這個方面頻頻發力。對於數據倉庫來說,CPU,內存,磁盤都可能稱為性能的瓶頸,但是我們指導磁盤的操作來說相比內存和CPU性能是最慢的,而列存儲恰恰是對IO的性能提升是很大的,列存儲會減少磁盤的IO操作,提升運算的效率,特別是大量數據的聚合。當然如果是一些線上的精確查找等操作,列存儲並不是最好的選擇。
對於這些性能的提升和存儲 空間的優化,主要是和列存儲的實現原理是分不開的(由於非聚集列存儲的功能比較雞肋,我們就不介紹了,因為有非聚集列存儲的表成為了一個只讀表):
1:Clustered columnstore inde – 整個表都按照列存儲進行組織,直接替代了傳統的堆表或者聚集索引,可以自由的進行增刪改操作。
2:聚集列存儲索引雖然相對於非聚集列存儲索引在column store這塊組織架構基本一樣,但是它可以進行增刪改操作。原因是它多了一塊或者多塊行存儲部分,這部分稱之為delta tore。
原理大概看完之后,下面給出SQL Server2014對列存儲的改進:
● 支持數據的讀和寫
● 在打破了數據只讀的限制后,列存儲索引使用的范圍和場景大大增加
● 相比傳統的ad-hoc的增刪改操作,在SQL Server2014還是推薦使用bulk insert和分區交換來進行大批次數據的更新,效率更高,維護成本也會降低
● 支持更多的數據類型
● 添加了更多的數據類型支持:(n)varchar(max), varbinary(max), XML, Spatial, CLR
● 基本說來,SQL Server2014的列存儲支持所有的non-blob數據類型
● 整個表可建立並且只能建立一個聚集列存儲索引。傳統的行存儲會需要非聚集索引幫助提高訪問效率,但是列存儲無需這樣。並且由於只有一份數據,因此存儲需要的磁盤空間大大降低
● 非聚集列索引仍然支持,並且還是只讀的結構。
當我們有了聚集列存儲索引后,就不需要非聚集列索引了,因為此時所有的數據都是按照列存儲了。但是如果表上需要添加Constraints或者工作負載仍然需要B-tree形式的非聚集索引,那么我們還是只能考慮使用非聚集列存儲索引。
● 語句的執行上有以下改進
○ 基於矢量的計算方式得到改
○ 支持更多的語法
■ 所有的join方式(包括OUTER, HASH, SEMI (NOT IN, IN)
■ UNION ALL
■ Scalar aggregates
■ “Mixed mode” plans
● 對bitmap和spill操作有進一步的改進
● 對hash join有所改進
其實我在SQL Server 2014列存儲的實踐當中,還發現有幾個不是非常友好的地方
1:SQL Server 2014聚集列存儲並不支持視圖功能,這個還是比較坑的,因為列存儲的主要應用場景就是數據倉庫,有很多視圖來說要提供報表或者提供給報表部門查詢權限,通過視圖能夠隱藏很多敏感信息,而不支持視圖就會很難做決定來具體修改為列存儲了
2:SQL Server 2014聚集列存儲並不支持alwayson從庫的查詢
3:SQL Server 2014 12.0.2版本對列存儲有漏洞,alwayson日志同步的時候容易造成內部鎖爭用,影響主從的同步,這點功能我們可能要升級SP1補丁才能解決,我這邊從庫升級后至今沒有出現這個問題,這也是SQL Server 2014列存儲的一個BUG吧
以上也算是在生產環境走過的坑,因為考慮不是很周全走了不少路。希望大家能夠引以為戒。除了以上幾個坑以外,列存儲還不支持以下的功能:
在列存儲索引中不可使用以下數據類型:
binary(n)、varbinary(n)(在2014及更高版本中允許使用,但不包括varbinary(max)),image、text、ntext、varchar(max)、nvarchar(max),sql_variant,xml
只能通過刪除及創建索引的方式重建索引,而不可使用ALTER INDEX命令
在視圖或索引視圖中無法使用列存儲索引
列存儲索引無法結合使用以下特性:分發,變更數據捕獲,變更追蹤,Filestream
列存儲索引不可包含多於1024個列
對應的表不可包含唯一性約束、主鍵約束或外鍵約束
接下來我們看一下列存儲的一些實踐:
1:創建列存儲的表
CREATE TABLE maxiangqian( id [int] NOT NULL, age [int] NOT NULL, sex [tinyint] NOT NULL, name varchar(20)); GO CREATE CLUSTERED COLUMNSTORE INDEX cci_Simple ON maxiangqian; GO :
2:行聚集索引轉換為列存儲:
CREATE TABLE maxiangqian( id [int] NOT NULL, age [int] NOT NULL, sex [tinyint] NOT NULL, name varchar(20)); GO CREATE CLUSTERED INDEX cl_simple ON maxiangqian (id); GO CREATE CLUSTERED COLUMNSTORE INDEX cl_simple ON maxiangqian WITH (DROP_EXISTING = ON);
或者說我們也可以直接刪除聚集索引,然后再
CREATE CLUSTERED COLUMNSTORE INDEX cl_simple ON maxiangqian
效果是一樣一樣的。
3 將一個堆表轉化為列存儲表:
第一步就是刪除堆表現有的索引,然后創建聚集列存儲索引:
CREATE TABLE maxiangqian( id [int] NOT NULL, age [int] NOT NULL, sex [tinyint] NOT NULL, name varchar(20)); GO create index pid on maxiagnqian(id) drop index pid on maxiangqian CREATE CLUSTERED COLUMNSTORE INDEX cci_Simple ON maxiangqian; GO :
上面基本上已經滿足你建立列存儲的一些功能,下面我們看一下怎么把一個聚集列存儲的表轉化為普通表:
CREATE CLUSTERED INDEX pid ON maxiangqian WITH ( DROP EXISTING = ON ); 或者 DROP INDEX cci_Simple ON MyFactTable;
OK,我們基本上已經可以知道怎么創建列存儲索引了
但是我們指導由於列存儲刪除的時候只是標記,所以說列存儲如果經常更新刪除,碎片還是會很大的,下面我們看下怎么消除碎片---重建:
CREATE CLUSTERED COLUMNSTORE INDEX cci_Simple ON maxiagnqian WITH ( DROP_EXISTING = ON ); ALTER INDEX cci_Simple ON maxiangqian REBUILD PARTITION = ALL WITH ( DROP_EXISTING = ON );
以上兩種方式是都可以實現的。
其實對於列存儲來說,卧鋪,我這邊給我比較大的驚喜就是磁盤空間的節約,列存儲的壓縮比例可以達到10:1甚至15:1,而且相對來說對於我數據倉庫一些大批量的聚合操作性能提升。在節省空間又提高性能的情況下,你還有什么理由不選用列存儲呢。
