在SQL Server中,數(shù)據(jù)的存儲以頁為單位。八個頁為一個區(qū)。一頁為8K,一個區(qū)為64K,這個意味著1M的空間可以容納16個區(qū)。如圖1所示:
如圖1(PS:發(fā)現(xiàn)用windows自帶的畫圖程序畫博客中的圖片也不錯)可以看出,SQL Server中的分配單元分為三種,分別為存儲行內(nèi)數(shù)據(jù)的In_Row_Data,存儲Lob對象的LOB_Data,存儲溢出數(shù)據(jù)的Row_Overflow_data。下面我們通過一個更具體的例子來理解這三種分配單元。
數(shù)據(jù)類型的選擇
在了解了一些基礎知識之后。我們知道SQL Server讀取數(shù)據(jù)是以頁為單位,更少的頁不僅僅意味著更少的IO,還有更少的內(nèi)存和CPU資源消耗。所以對于數(shù)據(jù)選擇的主旨是:
盡量使得每行的大小更小
這個聽起來非常簡單,但實際上還需要對SQL Server的數(shù)據(jù)類型有更多的了解。
比如存儲INT類型的數(shù)據(jù),按照業(yè)務規(guī)則,能用INT就不用BIGINT,能用SMALLINT就不用INT,能用TINYINT就不用SMALLINT。
所以為了使每行的數(shù)據(jù)更小,則使用占字節(jié)最小的數(shù)據(jù)類型。
1、比如不要使用DateTime類型,而根據(jù)業(yè)務使用更精確的類型,如下表:

2、使用VarChar(Max),Nvarchar(Max),varbinary(Max)來代替text,ntext和image類型
根據(jù)前面的基礎知識可以知道,對于text,ntext和image類型來說,每一列只要不為null,即使占用很小的數(shù)據(jù),也需要額外分配一個LOB頁,這無疑占用了更多的頁。而對于Varchar(Max)等數(shù)據(jù)類型來說,當數(shù)據(jù)量很小的時候,存在In-row-data中就能滿足要求,而不用額外的LOB頁,只有當數(shù)據(jù)溢出時,才會額外分配LOB頁,除此之外,Varchar(Max)等類型支持字符串操作函數(shù)比如:
● COL_LENGTH
● CHARINDEX
● PATINDEX
● LEN
● DATALENGTH
● SUBSTRING
3、對于僅僅存儲數(shù)字的列,使用數(shù)字類型而不是Varchar等。
因為數(shù)字類型占用更小的存儲空間。比如存儲123456789使用INT類型只需要4個字節(jié),而使用Varchar就需要9個字節(jié)(這還不包括Varchar還需要占用4個字節(jié)記錄長度)。
4、如果沒有必要,不要使用Nvarchar,Nchar等以“字”為單位存儲的數(shù)據(jù)類型。這類數(shù)據(jù)類型相比varchar或是char需要更多的存儲空間。
5、關于Char和VarChar的選擇
這類比較其實有一些了。如果懶得記憶,大多數(shù)情況下使用Varchar都是正確的選擇。我們知道Varchar所占用的存儲空間由其存儲的內(nèi)容決定,而Char所占用的存儲空間由定義其的長度決定。因此Char的長度無論存儲多少數(shù)據(jù),都會占用其定義的空間。所以如果列存儲著像郵政編碼這樣的固定長度的數(shù)據(jù),選擇Char吧,否則選擇Varchar會比較好。除此之外,Varchar相比Char要多占用幾個字節(jié)存儲其長度,下面我們來做個簡單的實驗。
首先我們建立表,這個表中只有兩個列,一個INT類型的列,另一個類型定義為Char(5),向其中插入兩條測試數(shù)據(jù),然后通過DBCC PAGE來查看其頁內(nèi)結構,如圖4所示。

圖4.使用char(5)類型,每行所占的空間為16字節(jié)
下面我們再來看改為Varchar(5),此時的頁信息,如圖5所示。

圖5.Varchar(5),每行所占用的空間為20字節(jié)
因此可以看出,Varchar需要額外4個字節(jié)來記錄其內(nèi)容長度。因此,當實際列存儲的內(nèi)容長度小于5字節(jié)時,使用char而不是varchar會更節(jié)省空間。
關于Null的使用
關于Null的使用也是略有爭議。有些人建議不要允許Null,全部設置成Not Null+Default。這樣做是由于SQL Server比較時就不會使用三值邏輯(TRUE,F(xiàn)ALSE,UNKNOWN),而使用二值邏輯(True,F(xiàn)alse),并且查詢的時候也不再需要IsNull函數(shù)來替換Null值。
但這也引出了一些問題,比如聚合函數(shù)的時候,Null值是不參與運算的,而使用Not Null+Default這個值就需要做排除處理。
因此Null的使用還需要按照具體的業(yè)務來看。
考慮使用稀疏列(Sparse)
稀疏列是對 Null 值采用優(yōu)化的存儲方式的普通列。 稀疏列減少了 Null 值的空間需求,但代價是檢索非 Null 值的開銷增加。 當至少能夠節(jié)省 20% 到 40% 的空間時,才應考慮使用稀疏列。
稀疏列在SSMS中的設置如圖6所示。

圖6.稀疏列
對于主鍵的選擇
對于主鍵的選擇是表設計的重中之重,因為主鍵不僅關系到業(yè)務模型,更關系到對表數(shù)據(jù)操作的的效率(因為主鍵會處于B樹的非葉子節(jié)點中,對樹的高度的影響最多)。關于主鍵的選擇,我之前已經(jīng)有一篇文章關于這點:從性能的角度談SQL Server聚集索引鍵的選擇,這里就不再細說了。
總結
本篇文章對于設計表時,數(shù)據(jù)列的選擇進行了一些探尋。好的表設計不僅僅是能滿足業(yè)務需求,還能夠滿足對性能的優(yōu)化。