簡介
sql server每個表中各列的資料型別的有各種形式,產生的效果也各有不同,我們主要根據效率兼顧效能的情況下討論下如何規定型別。
在sql server中,資料的儲存以頁為單位。八個頁為乙個區。一頁為8k,乙個區為64k,這個意味著1m的空間可以容納16個區。 sql server中的分配單元分為三種,分別為儲存行內資料的in_row_data,儲存lob物件的lob_data,儲存溢位資料的row_overflow_data。下面我們通過乙個更具體的例子來理解這三種分配單元。
我建立如圖2所示的表。
圖2.測試表
圖2的測試表不難看出,通過插入資料使得每一行的長度會超過每頁所能容納的最大長度8060位元組。使得不僅產生了行溢位(row_overflow_data),還需要儲存lob的頁.測試的插入語句和通過dbcc ind看到的分配情況如圖3所示。
圖3.超過8060位元組的行所分配的頁
除去iam頁,這1行資料所需要三個頁來儲存。首先是lob頁,這類是用於儲存存在資料庫的二進位制檔案所設計,當這個型別的列出現時,在原有的列會儲存乙個24位元組的指標,而將具體的二進位制資料存在lob頁中,除去text之外,varbinary(max)也是存在lob頁中的。然後是溢位行,在sql server 2000中,一行超過8060位元組是不被允許的,在sql server 2005之後的版本對這個特性進行了改進,使用varchar,nvarchar等資料型別時,當行的大小不超過8060位元組時,全部存在行內in-row data,當varchar中儲存的資料過多使得整行超過8060位元組時,會將額外的部分存於row-overflow data頁中,如果update這列使得行大小減少到小於8060位元組,則這行又會全部回到in-row data頁。
資料型別的選擇
在了解了一些基礎知識之後。我們知道sql server讀取資料是以頁為單位,更少的頁不僅僅意味著更少的io,還有更少的記憶體和cpu資源消耗。所以對於資料選擇的主旨是:
盡量使得每行的大小更小
這個聽起來非常簡單,但實際上還需要對sql server的資料型別有更多的了解。
比如儲存int型別的資料,按照業務規則,能用int就不用bigint,能用smallint就不用int,能用tinyint就不用smallint。
所以為了使每行的資料更小,則使用佔位元組最小的資料型別。
1.比如不要使用datetime型別,而根據業務使用更精確的型別,如下表:
型別 所佔位元組
date(僅日期)
3time(僅時間)
5datetime2(時間和日期)
8datetimeoffset(外加時區)
102.使用varchar(max),nvarchar(max),varbinary(max)來代替text,ntext和image型別
根據前面的基礎知識可以知道,對於text,ntext和image型別來說,每一列只要不為null,即使占用很小的資料,也需要額外分配乙個lob頁,這無疑占用了更多的頁。而對於varchar(max)等資料型別來說,當資料量很小的時候,存在in-row-data中就能滿足要求,而不用額外的lob頁,只有當資料溢位時,才會額外分配lob頁,除此之外,varchar(max)等型別支援字串操作函式比如:
3.對於僅僅儲存數字的列,使用數字型別而不是varchar等。
因為數字型別占用更小的儲存空間。比如儲存123456789使用int型別只需要4個位元組,而使用varchar就需要9個位元組(這還不包括varchar還需要占用4個位元組記錄長度)。
4.如果沒有必要,不要使用nvarchar,nchar等以「字」為單位儲存的資料型別。這類資料型別相比varchar或是char需要更多的儲存空間。
5.關於char和varchar的選擇
首先我們建立表,這個表中只有兩個列,乙個int型別的列,另乙個型別定義為char(5),向其中插入兩條測試資料,然後通過dbcc page來檢視其頁內結構,如圖4所示。
圖4.使用char(5)型別,每行所佔的空間為16位元組
下面我們再來看改為varchar(5),此時的頁資訊,如圖5所示。
圖5.varchar(5),每行所占用的空間為20位元組
因此可以看出,varchar需要額外4個位元組來記錄其內容長度。因此,當實際列儲存的內容長度小於5位元組時,使用char而不是varchar會更節省空間。
關於null的使用
關於null的使用也是略有爭議。有些人建議不要允許null,全部設定成not null+default。這樣做是由於sql server比較時就不會使用三值邏輯(true,false,unknown),而使用二值邏輯(true,false),並且查詢的時候也不再需要isnull函式來替換null值。
但這也引出了一些問題,比如聚合函式的時候,null值是不參與運算的,而使用not null+default這個值就需要做排除處理。
因此null的使用還需要按照具體的業務來看。
考慮使用稀疏列(sparse)
稀疏列是對 null 值採用優化的儲存方式的普通列。 稀疏列減少了 null 值的空間需求,但代價是檢索非 null 值的開銷增加。 當至少能夠節省 20% 到 40% 的空間時,才應考慮使用稀疏列。
稀疏列在ssms中的設定如圖6所示。
圖6.稀疏列
更具體的稀疏列如何能節省空間,請參看msdn。
對於主鍵的選擇
對於主鍵的選擇是表設計的重中之重,因為主鍵不僅關係到業務模型,更關係到對錶資料操作的的效率(因為主鍵會處於b樹的非葉子節點中,對樹的高度的影響最多)。這個我們得結合主鍵索引的選擇來具體分析,之前寫過一篇關於索引的,以後有需要再進一步延伸來講
總結
本篇文章對於設計表時,資料列的選擇進行了一些探尋。好的表設計不僅僅是能滿足業務需求,還能夠滿足對效能的優化。
關於jsp亂碼的一點小分析
jsp頁面亂碼可能會出現在下面幾個地方 1 jsp頁面編碼 2 html頁面編碼 3 jsp文件編碼 4 資料庫編碼 5 瀏覽器的編碼問題 解決辦法 以utf 8為例 1 給jsp page標籤內部加上 pageencoding utf 8 給 內部加上request.setcharacterenc...
遞迴函式的一點分析
1.主要目的 為了知道遞迴函式返回數值時的順序 2.實驗 使用最簡單的階乘來實驗 對遞迴函式的輸出順序的實驗 用到階乘 include include int main int n jiecheng int n return s 3.輸出結果 enter an integer 1 to 12 i w...
nDPI原始碼的一點分析
ndpi是開源的深度包檢測庫,更詳細的介紹,請參考ndpi 官網 每種應用層協議的檢測的流程 ndpi detection process packet check ndpi flow func check ndpi tcp flow func check ndpi udp flow func或ch...