SQL Server 列存儲索引性能總結(1)——環境準備及簡介

列存儲是從SQL 2012引入,並在後面發揮非常大性能提升的功能,因爲當前工作過程需要使用,並且已經出現了很多問題,所以這裏我打算把這兩個月的經驗總結和分享一下。
這個系列跟前面的有些類似,但是我希望能夠把零碎的知識點合在一個系列中,最起碼方便自己以後搜索。

環境準備

   本文會使用SQL Server On Linux環境,不過這個問題不大,SQL Server 使用2019版本。
   OS爲20G內存,4 core。CentOS 7.7。
   演示數據庫使用ContosoRetailDW,由於後續會反覆使用這個庫,建議保留備份文件,每次操作完就還原。使用大概3千萬數據作爲測試。部分單獨測試會使用新建數據庫或者TempDB。
   本系列使用Hyper-V的虛擬機做測試,所以I/O不會很高,它的值不能作爲什麼衡量標準,但是可以跟其他方式做橫向對比。

列存儲簡介

   列存儲需要記住的一些關鍵點:

  • 列存儲設計之初是爲了提升數據倉庫的性能。
  • 從SQL 2012引入非聚集列存儲索引,但是不可更新,禁止修改底層schema,不可使用唯一約束,批執行模式受限等,都使得列存儲索引沒有很好地推廣起來。
  • 從SQL 2014開始,引入了聚集列存儲索引。它主要有以下提升:
  1. 可更新。
  2. 可修改schema。
  3. 支持更多數據類型。
  4. 混合執行模式(批和行)。
  5. 支持更多批執行模式。
  6. 更好的壓縮效果。
  7. 支持seek操作
  8. 支持和提升bulk insert中的spill。
  • 基本技術點是把數據拆分並存儲到不同的行組(row groups),也叫片段(segments),每個片段應該包含大概100萬行數據。
  • 每一列都有自己的片段組,是基於列,而不是傳統行存儲那樣基於行。
  • 行組的數據經過編碼和壓縮後,轉成Blobs(因此在SET STATISTICS IO中需要看lob logical reads和lob physical reads)然後存儲到SQL Server中。底層存儲大小依舊是8K的頁。如果一個Blob已經大於1個頁,會調用通用的Blobs存儲機制。
  • 使用sys.column_store_segments 這個DMV可以查到相關的元數據信息。
  • Delete bitmap:一個非常重要的信息,在前兩個月就是因爲這個導致我公司的Azure SQL DB 不可用了。這是關於片段中被刪除的數據行的信息。後面有機會再講。
  • 聚集列存儲索引的架構總的來說由兩部分組成:1. 行組/片段。2. Delta Stores(增量存儲)。增量存儲是一個常規的b-tree行存儲,所以不要以爲建了聚集列存儲索引之後就一定能用上它的優點。 如果大量的數據都存儲在Delta Stores中,那麼還是跟傳統行存儲索引一樣。使用DMV:sys.column_store_row_groups 可以查看所有片段和增量存儲的信息。DMV中的值,片段的status=3,並且描述是Compressed,Delta Stores的status可能是1或者2,描述爲Open/Close。Open意味着增量存儲還能添加數據,Close爲正在準備轉換到片段。
  • 列存儲術語:
    Insert:添加數據到當前open的Delta Stores中。
    Delete:如果刪除的數據是在片段中,那麼Deleted Bitmap會更新並帶有對應的row id。如果是在增量存儲中,那麼直接從B-tree結果中移除。
    Update:實際過程就是先刪除再insert。

相關知識

   除了片段、行組、增量存儲等概念之外。在使用過程中我還見到了一些術語,這裏也列一下做備用:

  1. Tuple Mover:
       當我們插入數據到一個聚集列存儲索引表時,也就是Delta Store,對於常規insert(100萬行)或者Bulk insert(10萬行),到達臨界點時就會觸發這個Delta Store被填滿並關閉,然後創建新的Delta Store供其他insert。
       SQL Server 有一個後臺進程會自動檢測是否有已經關閉的Delta Store(status=2),如果找到,就會創建一個片段對Delta Store編碼和壓縮,這個後臺進程叫Tuple Mover。默認情況下每5分鐘執行一次。
       Tuple Mover不阻塞任何掃描讀,但是並行刪除和更新必須等待到壓縮過程完成。這個阻塞對於高併發高負載的OLTP系統而言是有明顯影響的,所以說列存儲並非爲OLTP設計的。
       已經被壓縮的片段/行組可以插入新的行,只要沒有達到大概100萬的限制。其中一個調用Tuple Mover的方式是執行索引重組(alter index reorganize)。也可以直接alter table rebuild或者alter index rebuild。
  2. Bulk insert:
      導入數據從來都是難點,我所在的項目現在就遇到這個問題。對於聚集列存儲索引的Bulk insert而言,10萬行是臨界點,如果插入90000行數據,那麼會創建一個新的Delta Store或者插入到現有的但是能容納這批數據的Delta Store中。但是如果插入11萬行,就會自動轉換到片段/行組中。理想的數量應該是超過100萬行,這會自動轉換到一個不可更新的和已滿的片段中。
      Bulk insert本身是非常高效的,它可以全部加載到內存然後進行編碼和壓縮,然後存儲數據到磁盤。前提是內存足夠。從SQL 2014起,insert into … select from …命令就被優化成可以使用並行批執行模式的bulk insert操作。
  3. 隔離級別:
      除了Snapshot之外,其他現有隔離級別都支持聚集列存儲索引。提到這個是因爲Snapshot經常會用在數據倉庫中,所以要注意一下。

計劃進行的實驗

   因爲準備可用性組,也就是alwayson,搭建讀寫分離比較耗時和耗機器,所以暫時不打算做這部分的測試。

  1. (不分區)導入數據到堆表然後創建聚集列存儲索引。
  2. (不分區)直接導入數據到聚集列存儲索引表。
  3. (不分區)使用Bulk insert導入數據到聚集列存儲索引表。
  4. (不分區)使用Bulk insert導入數據到堆表然後創建聚集列存儲索引。
  5. (不分區)使用並行插入到堆表然後創建聚集列存儲索引。
  6. (分區)導入數據到堆表然後創建聚集列存儲索引。
  7. (分區)直接導入數據到聚集列存儲索引表。
  8. (分區)使用Bulk insert導入數據到聚集列存儲索引表。
  9. (分區)使用Bulk insert導入數據到堆表然後創建聚集列存儲索引。
  10. (分區)使用並行插入到堆表然後創建聚集列存儲索引。
  11. (不分區,in-memory)導入數據到堆表然後創建聚集列存儲索引。
  12. (不分區,in-memory)直接導入數據到聚集列存儲索引表。
  13. (不分區,in-memory)使用Bulk insert導入數據到聚集列存儲索引表。
  14. (不分區,in-memory)使用Bulk insert導入數據到堆表然後創建聚集列存儲索引。
  15. (不分區,in-memory)使用並行插入到堆表然後創建聚集列存儲索引。

   下一篇:SQL Server 列存儲索引性能總結(2)——獲取元數據信息

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章