執行SQL查詢時,主要的幾個瓶頸在於:CPU運算速度、內存緩存區大小、磁盤IO速度。而對於大數據量數據的查詢,其瓶頸則一般集中於磁盤IO,以及內存緩存。那麼為了提高SQL查詢的效率,一方面我們需要考慮盡量減少查詢設計的數據條目數——建立索引,設立分區;另一方面,我們也可以考慮切實減少數據表物理大小,從而減少IO大小。
在SQL Server 2008中,最新提供了一項功能“壓縮(Compression)”,就是定位於減少數據表、索引物理大小。
設置壓縮
在企業管理器中,在需要壓縮的表或索引上右鍵選擇Storage-》Manage Compression:
這裡可以看到幾點:
下方列表裡列出了該表所有的分區,也就是可以同一張表的不同分區應用不同的壓縮策略。
壓縮方式(Compression Type)分為Row和Page兩種。
行級壓縮(Row):
一方面減少了動態長度字段元數據的大小(varchar、varbinary等),比如之前存儲字段實際長度需要2bytes,壓縮後只需要3bits。
另一方面也直接減少各字段存儲內容的大小,比如存儲數值1在一個int類型字段中,壓縮後只占用了一個字節。
頁級壓縮(Page):
能在各行間共享相同的數據,這裡面包含兩項技術:列前綴(Column Prefix)、頁字典(Page Dictionary)。
列前綴
可以讓擁有同樣前綴的字段值擁有類似外鍵一樣的結構來存儲相同的前綴和各自的其余部分。比如一張存儲了一個網站所有頁面URL的表,URL字段存儲的值分別是‘www.example.com/a.html’,‘www.example.com /b.html’,‘www.example.com/c.html’,‘www.example.com/d.html’。則壓縮後,它們同樣的前綴 ‘www.example.com/’會被提取出來,而其余部分會被類似如下的形式存儲 ‘1a.html’,‘1b.html’,‘1c.html’,‘1d.Html’。
頁字典
則可以將在應用列前綴基礎上的其余部分再次聚合存儲,比如同樣是一張存儲了一個網站所有頁面URL的表,假設有在表裡裡有多條URL字段的值相同,比如 ‘1a.html’,‘1b.html’,‘1c.html’,‘1b.html’,‘1a.html’,‘1a.html’,則通過頁字典技術壓縮後,實際存儲在字段中的值會進一步減少為‘2’,‘3’,‘1c.Html’(沒有重復的字段值不會被壓縮),‘3’,‘2’,‘2’。
點擊“Calculate”後,會計算出表當前占用的空間大小,以及
壓縮需要的空間大小
。注意這裡與一般預想的不同,如果要對一張預存有數據但尚未壓縮的表進行壓縮,首先需要的是額外的空間大小。
執行壓縮
設置好之後,就可以選擇是生成腳本還是立即執行,一般壓縮的執行時間受表原有數據多少以及選擇壓縮方式的影響。筆者對一張有上千萬條記錄的表做頁級壓縮,耗時在10分鐘左右。
壓縮完成之後查看數據庫大小,會發現數據庫的大小變大了!這也和在設置階段計算出來的額外空間相關。但實際上這裡大部分空間是預占的空間,並沒有實際數據。如果需要節省磁盤空間,需要進一步執行
收縮(Shrink)
操作。
與 Compression不同,Shrink用來釋放數據庫占據的沒有利用的空間,一般用來對無用的日志文件收縮(如果操作頻繁,日志文件很有可能大於數據庫實際數據的大小)。這裡我們對數據庫文件(mdf)做Shrink操作,完成之後再看數據庫的大小,果然減少了很多。筆者做壓縮、Shrink之後,一般都能將數據庫的大小減為原來的1/3~1/2左右。當然,具體壓縮比率取決於壓縮方式、壓縮表的字段特點、壓縮表占整個數據庫數據的比重等。
注意事項
既然對表行了壓縮,那麼在執行查詢時必然會有解壓縮的過程。而這一過程會占用CPU時間,也就是我們在通過壓縮減少了磁盤占用空間以及IO時間的同時,增大了CPU的消耗。所以在壓縮前需要考慮清楚查詢的瓶頸到底是磁盤IO還是內存還是CPU。而且如果表應用了壓縮,類似建立索引,對於增刪改等操作也會有一定的影響。所以同樣要考慮應用在表上的操作到底以哪種為主。
各頁面的壓縮是獨立進行的,頁字典和列前綴也分別存儲於各頁內。而且壓縮僅在數據頁快滿的時候進行,因為一個頁的大小是固定的,壓縮半頁不會有性能上的提升。
數據庫備份中也有Compression的選項,但這利用的是系統的文件壓縮技術,而且只能應用於整個數據庫上。
容易被忽略的是,索引也能被壓縮,而且和表壓縮獨立,同樣也會提升所有應用到索引的查詢的性能。
在Shrink階段,可能會造成大量的索引碎片,所以可以在Shrink完成之後重建或者重組織索引,但同時,這些操作也會造成數據庫的體積變大……也就是,最小的數據庫體積和最小碎片比率的索引是魚與熊掌,不可兼得。