數據行所占的最小空間是多少呢? 先來看看下面這張經典數據行結構圖(引自Inside Sql Server) 因為可變長度類型的列會有額外的空間開銷,所以不考慮可變長度的字段。 現在計算一下除去實際數據后每個數據行所需要占用的空間: 狀態位A占一個字節,狀態位B占
數據行所占的最小空間是多少呢?
先來看看下面這張經典數據行結構圖(引自Inside Sql Server)
因為可變長度類型的列會有額外的空間開銷,所以不考慮可變長度的字段。 現在計算一下除去實際數據后每個數據行所需要占用的空間:
狀態位A占一個字節,狀態位B占1個字節,用于標識固定字段長度的占2個字節,標識固定列數量的占去2個字節,NULL位圖至少占用1個字節。
這樣算下來:1+1+2+2+1=7。 也就是在不考慮實際數據的情況下一個數據行最少要占用7個字節。如果實際數據長度為1,那么每一行所占用的空間就為8個字節。真的是這樣嗎?
現在做個實驗驗證一下,在下文中如果沒有特殊的說明,所有的敘述都是以數據表沒有建立聚集索引為前提的。
創建一個表:
Create Table table1
(
col1 char(1)
)
插入2行數據:
insertinto table11 values('a')
insertinto table11 values('b')
然后使用DBCC PAGE命令察看表的結構(為了方便察看,我只保留了Data和Offset Table部分)
DATA:
Slot 0, Offset 0x60, Length 9, DumpStyle BYTE
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP
Memory Dump @0x44EFC060
00000000: 10000500 610100fe 08†††††††††††††††††....a....
Slot 1, Offset 0x69, Length 9, DumpStyle BYTE
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP
Memory Dump @0x44EFC069
00000000: 10000500 620100fe 08†††††††††††††††††....b....
OFFSET TABLE:
Row - Offset
1 (0x1) - 105 (0x69)
0 (0x0) - 96 (0x60)
然后使用DBCC PAGE命令察看表的結構(為了方便察看,我只保留了Data和Offset Table部分)
DATA:
Slot 0, Offset 0x60, Length 9, DumpStyle BYTE
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP
Memory Dump @0x4500C060
00000000: 10000600 61610100 fe†††††††††††††††††....aa...
Slot 1, Offset 0x69, Length 9, DumpStyle BYTE
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP
Memory Dump @0x4500C069
00000000: 10000600 62620100 fe†††††††††††††††††....bb...
OFFSET TABLE:
Row - Offset
1 (0x1) - 105 (0x69)
0 (0x0) - 96 (0x60)
通過上面兩個實驗可以看出:只有一個字段,并且類型和長度為char(1)的表的數據行所占用的空間和只有一個字段,并且類型和長度為char(2)的表的數據行所占用的空間是相等的。為什么會出現這種情況呢?
實際上,如果一個數據行的長度沒有達到規定的最小長度(9 bytes),SQL SERVER會自動在該行的后面填充一個字節,將長度擴展到9。這就是我們之前看到的那個08出現的原因。所以SQLSERVER數據行最小的長度為9,而不是8。
您可能要問了:為什么SQL SERVER會有這樣的規定呢?
這個規定是為了更新操作(update)建立的龜腚。我們知道RID用來標識數據頁數據行,當數據頁中的某一行數據被更新以至于現有的數據頁無法再容納該行時,SQLSERVER會將該行移動到新的數據頁中。原有的數據行位置并不被其他的數據行占用,而是替換成一個forwarding pointer。該pointer指向那個被更新的數據行的新的位置。而這個pointer大小為9 byte(header占一byte,RID占8byte,RID的構成:4個字節的pageID,2個字節的fileID,2個字節的slotID)。所以sql server為了保證能夠成功地將數據行替換成forwarding pointer,規定每個數據行要最少要占用9個byte。
您可能又要問了,為什么需要那個鳥forwarding pointer呢,直接移動不就ok了。反正數據頁和數據頁之間是沒有關系的,數據頁中的數據行又是無序的。要那個forwarding pointer有啥用???
原因是這樣的,當數據頁中的某一行數據被更新以至于現有的數據頁無法再容納該行時,SQLSERVER會將該行移動到新的數據頁中。之后就出現了兩個選擇:
1 更新原有數據頁中所有數據行的RID,這可能是個非常昂貴的操作(可能有249個索引)。
2 不更新數據頁中的RID,將那個空缺的位置替換成一個forwarding pointer。在以后的查詢時按照這個pointer查找紀錄。
您可能還有疑問:如果表建立了聚集索引,每個數據行也要占用9byte嗎
是這樣的。雖然聚集索引的更新操作有所不同,但是為了方便b-tree轉換成heap,也需要保持9個byte
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com