predicate 列存储索引扫描_ColumnStore index (列存储索引)解析
簡介
首先介紹列存儲的概念: 傳統的數據庫存儲是行存儲。對于SQL Server來說,每個page是8K;往page里面塞數據,假設該表每條數據長度是500字節,那么這個page 先塞第一條數據,然后再塞第二條數據,大概能塞 8K/500=16條數據。注意這里每一條數據都是包括所有字段(column)的。如圖所示,下面是若干個page,每個page塞滿了一行一行的數據。行存儲示意圖。
接下來介紹列存儲,是一個column一個column塞,而且在SQL Server里,是向row group或者segment塞。列存儲示意圖
如圖,該表有5個字段(column),每個紅色柱體是一個Segment;每5個segment組成一個row group。Segment只包含一個column的數據,而row group包含所有column的數據。
每個Segment最多包含1百萬條該column的數據;從性能上說,1百萬條能達到性能最優(數量越大,壓縮比越大),數量越少,該segment的性能越差。
列存儲對比行存儲有什么好處: 行存儲適合OLTP系統,就是多用戶使用,很多update/insert/delete,SQL 語句處理的對象都是幾條數據或者幾百條數據(數據量不大);
列存儲適合數據倉庫,用戶數很少,數據量巨大,數據變化少(除了ETL)。在SQL Server中,ColumnStore index能把大量的數據壓縮到1/10,從而減少IO,CPU和memory的使用,從而帶來性能的飛躍;ColumnStore 除了壓縮,還使用了Batch mode,segment eliminate等技術,對性能有很大提升。
2. ColumnStore Index 適用的場景
和其他技術一樣,不能適合所有場景;如果選用的場景不適合,反而會帶來性能的急劇下降。
(1) 用星性/雪花模型建模的數據倉庫
(2) 該表(或者分區)的記錄數要大于1百萬
(3) 大部分SQL 語句是報表類的語句,就是range scan 而不是 seek。
(4) 該表的數據很少進行update/delete,大量的insert是可以的。
(5) 該表不能有varchar(max), nvarchar(max), or varbinary(max) 數據類型
(6) 對于OLTP數據庫,在某些特定的場景下也可以使用columnstore index :real-time operational analytics
3. Column Store的物理結構
ColumnStore Index 除了Row group 還包括 DeltaStore。假設該表有1105萬條記錄,每個rowgroup容納100萬條,那么總共有11個row group,還有5萬記錄放在Deltastore里面。
DeltaStore是用來存放不夠數量(這里是100萬)的數據,行存儲,沒有壓縮;而rowgroup都是列存儲,而且壓縮了。
隨著insert 數據增多,Deltastore的數量增加,如果數量增加到100萬,該Deltastore 會停止接收數據,變成row group,也就是 列存儲,壓縮;如果還有數據insert,會生成新的Deltastore。
Columnstore Index 的組成部分除了 Row Group,Delta store,還有 Delted Bitmap.
Columnstore index 刪除記錄并不是物理刪除,而是邏輯刪除,在 Delted Bitmap加一個標記; Delted Bitmap會記錄整個表被刪除的記錄;SQL Server對該表做query的時候,除了查詢row group,Delta store(row store)還要查詢 Delted Bitmap,把三者的結果Union才是最后的結果。
那么ColumnStore Index什么時候做物理刪除? 對index 進行rebuild或者reorganize的時候。
對Columnstore index進行update,并不是物理update,而是delete該條記錄然后insert一條新的記錄。
ColumnStore Index 結構小結:
(1) 包括Row Group(compressed,列存儲),Delta Store (也叫Delta Row group,行存儲),Deleted Bitmap (存儲被刪除的記錄的信息)。
(2) 從SQL Server2016開始,一個表創建了ColumnStore index后,還可以創建傳統的行存儲的索引----non clustered index(NCI)。
(3)ColumnStore index本身不排序的,所以查詢某一條記錄都需要 全表掃描(full table scan);不排序這個特性對于insert是利好,performance很好;對于delete/update不好,特別表比較大的時候。 因為delete/update某一條記錄,需要先找到它,而查找的代價對于ColumnStore Index 比較大。
(4) 對于上面第三點,要快速的找到某一條或幾條數據,可以在ColumnStore index基礎上再創建傳統的行存儲的索引----non clustered index(NCI)。
(5) 在ColumnStore index基礎上再創建傳統的行存儲的索引----non clustered index(NCI),好處不僅是performance,還能給這個表加上 唯一性約束、主鍵約束和外鍵約束等約束。當然,這些都只能在SQL2016或之后的版本才能實現。
4. 如何發現某些表適合創建ColumnStore index。
首先它比較適合于數據倉庫,但數據倉庫的每個表都能創建ColumnStore Index嗎?另外OLTP環境可以使用CCI嗎?
另外可以用 DMV sys.dm_db_index_operational_stats來查看某個表的過往操作:
(1) 如果50%以上的操作是range scan,而不是seek
(2) update/delete的操作少于10%
那么這個表很適合創建cluster columnstore index(CCI);當然還有一個前提,該表足夠大,至少1百萬條記錄,越大越好。
如何估計某個表創建clustered columnstore index 之后的壓縮率? 在SQL 2019中,可以使用sp_estimate_data_compression_savings 來預估壓縮率。
該sp在數據庫中執行以下操作:
?創建臨時表 T
?把該表的數據進行采樣,載入一部分數據到T
?查看T的大小
?對T 進行列存儲壓縮,查看壓縮后的大小
5. 快速的裝載數據到已經創建 CCI的表中
(1) 裝載外部文件
bulk insert tableA FROM 'c:\temp\fileA.csv'
csv文件的數據并行的裝載到多個Row Group和多個Delta Store 中;超過102400條記錄的數據進入Row Group,列壓縮;沒有超過102400條記錄的數據進入 Delta Store (Delta Row group)。
因為Row Group提供了高的壓縮比,所以裝載數據產生的日志也會少很多;
SQL Server會自動的使用并行操作,同時向多個Row Group裝載數據。
(2) 從其他表裝載數據
Insert into select * from
與“裝載外部文件”很類似,超過100k條記錄的數據進入Row Group,列壓縮;沒有超過100k條記錄的數據進入 Delta Store (Delta Row group)。
不過SQL Server不會自動的使用并行操作,要使用tablock才能觸發并行。
insert into ccitest with (TABLOCK) select * from dbo.FactResellerSalesXL (能并行)
(3) 使用SSIS 來裝載數據
從SQL2016開始,有一個新的參數 AutoAdjustBufferSize,它能根據batchsize來自動調整 buffer size,對性能有極大提升。
6. CCI 的性能
CCI 除了壓縮比大帶來的CPU/memory/IO的減少,還使用了 Batch mode,segment eliminate和 并行來提升性能。
(1) Batch Mode
Batch mode 從字面上,就是批處理,就是一次處理幾百條數據,而不是一條一條處理數據;
比較適合于大數據量的數據倉庫。
Batch mode 是從SQL Server 2012開始和ColumnStore Index 一起使用的;在SQL 2019之前,Batch mode只能在列存儲中使用,從SQL 2019開始,batch mode也能在row store使用。
對于 SQL 語句: selectProductKey,OrderQuantityfromFactResellerSalesXL_CCI where OrderQuantity<3
batch mode會在內存中 占用64K大小的內存,形成上圖中的vector,先scan 該表把64k的數據放到vector中,然后用predictte來過濾(OrderQuantity<3)。符合過濾條件的,會在memory當中的bitmap打上標記;bitmap在上圖的最左邊。
每次處理64k大小的數據。
在SQL 2016之前,很多函數不支持 batch mode,包括sum,avg,min,rank等;還有distinct,left join,group by, order by等也不被支持。 總之一句話,要用Columnstore index ,要用batch mode,不要選SQL 2012/2014,要選SQL 2016/2017/2019
(2) Segment elimination and column elimination
Segment elimination 也叫 Rowgroup elimination
從上圖可知,如果表SalesTable創建了ColumnStore Index,SQL Server會自動把不需要的字段(column),不需要的rowgroup過濾掉。
CCI如何過濾Row group/segment?請看下面的元數據:
SELECT segment_id, object_name(p.object_id), s.column_id, s.min_data_id, s.max_data_id FROM sys.column_store_segments s, sys.partitions p
where p.object_id = object_id('FactResellerSalesXL_CCI') and
p.hobt_id = s.hobt_id and column_id = 2
從上圖可以看到,該表的CCI的第二個字段(column)有12個segment,每個segment都記錄了最大值,和最小值,這樣sql語句查詢的時候,很容易過濾不需要的segment。
另外執行 下面的語句:
set statistics IO ON
set statistics TIME ON
SELECT Productkey, OrderQuantity as curqty,
Sum (OrderQuantity) OVER (ORDER BY ProductKey) AS TotalQuantity
FROM FactResellerSalesXL_CCI WHERE orderdatekey in ( 20060301,20060401)
正因為第二個字段(orderdatekey)的元數據存儲了最大值/最小值,所以上面的SQL 直接skip了 7個segment,只在另外的5個segment中查找數據
(3) Aggregate Pushdown
從SQL2016 開始,對于Select SUM(sales) from 這樣的語句,當表非常大的時候,Aggregate Pushdown 特性可以極大提高性能。
簡單的來說,對于sum/avg/group by/max/count這樣·的函數,表有數以億計的記錄,SQL Server 會自動的把處理大量數據的工作放在執行計劃的第一步,也就是最接近存儲的地方,這樣傳送給執行計劃的下一步的數據會大大減少。
該特性自動進行,不用任何調整。
7. CCI 的維護。
和其他傳統的row store index一樣,CCI也會有碎片(fragment)。
有兩類碎片:
1.對于CCI,只有一個Delta Store是正常的,如果有多個Delta Store(超過10個以上)那就是碎片化,需要進行維護
2. 對于列存儲的Row Group,刪除數據只是邏輯刪除,沒有物理刪除;如果刪除的數據占該RowGroup中超過10%,那就是碎片化,需要進行維護。
從SQL 2016開始,使用 Alter Index on
(1) self merge
當Row group中的邏輯刪除記錄數占到10%以上,就會使用self merge物理刪除這些記錄;
(2)merge
兩個Row group的記錄數加起來都不到1百萬,那么merge操作會把這兩個RG 合并成一個
8. Columnstore 和 In-Memory OLTP的結合
首先解釋什么是 Real-time Operational Analytics。
之前介紹的傳統的數據倉庫,適合使用 CCI;對比傳統的數據倉庫,現在有一些混合型的應用場景,就是既有OLTP,也有數據倉庫的查詢。這種場景,就是直接在OLTP的數據庫上跑一些報表的大SQL,好處如下:因為沒有專門的數據倉庫,節約了硬件;
沒有經過ETL,OLTP的數據一般是最新的,而傳統的數據倉庫往往經過ETL,數據往往不是最新的。
因為沒有ETL,Stage 數據庫的硬件,ETL的維護、軟件的成本都節省了。
缺點也很明顯:不能像傳統數據倉庫,有多個數據源
沒有經過ETL,數據的結構不能像傳統數據庫那樣實現星形/雪花建模
同時在一個數據庫上跑OLTP和報表SQL,互相影響性能
前面介紹了,Real-time Operational Analytics可以使用disk based table(磁盤表),也可以使用in memory table;如果使用前者,就可以使用noclustered columnstore index;如果使用in memory table,必須使用 clustered columnstore index。下面介紹后者
首先該表是 in-memory OLTP table,表上可以創建hash index 或range index,這些都是傳統的row store(行存儲),只是把這些都搬到內存當中;圖中最下面是columnstore index(列存儲),也是放著memory當中。
上圖告訴我們,數據實現了冗余,in memory table存儲了數據,而內存中的columnstore index也存儲了數據,而且是數據、索引放在一起。每當有數據insert,先對in memory table的hot 部分(尾部)進行insert,當這一部分的記錄數達到1百萬,這些數據會轉移到columnstore index 當中。
當SQL 語句是OLTP類型,in memory table 可以很好的處理,效率非常高,具體原理要參考in memory OLTP特性。
當SQL 語句是報表類語句,columnstore index可以高效處理。
從上圖看,該場景需要比較多的memory。
總結
以上是生活随笔為你收集整理的predicate 列存储索引扫描_ColumnStore index (列存储索引)解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓系统收费标准(安卓系统收费)
- 下一篇: 我的世界java无法安装包_手把手教你搭