以下來自Woodytu的sqlserver存儲系列,一共八篇,記錄下來學習
https://www.cnblogs.com/woodytu/p/4486193.html
SQL Server 存儲(2/8):理解數據記錄結構
在SQL Server :理解數據頁結構我們提到每條記錄都有至少7 bytes的系統行開銷,那這7 bytes行開銷到底是一個什麼樣的結構,我們一起來看下。
數據記錄存儲我們具體的數據,換句話說,它存在堆表裏,或者存在聚集索引的葉節點。數據記錄結構是爲了讓SQL Server更高效的管理數據。
我們來看下數據記錄結構示意圖:
上圖中藍色部分是所有數據記錄部分(即系統行開銷,大小基於列個數,等於或大於7 bytes),綠色部分是表結構裏取決於定長/變長列的數據記錄部分(實際存放的數據,大小基於實際數據)。
行頭系統數據
用作狀態位1的第1字節(8位)用來定義記錄的屬性:
- 第0位:版本信息,在SQL Server 2008裏始終是0;
- 第1-3位:這3位用來定義記錄類型;
- 0 數據記錄(data record)
- 1 轉發記錄(Forwarded record)
- 2 轉發存根(a forwarding stub)
- 3 索引記錄(Index record)
- 4 二進制堆碎片或行溢出數據(blob fragment or row overflow data)
- 5 鬼影索引記錄(ghost index record)
- 6 鬼影數據記錄(ghost data record)
- 7 鬼影版本記錄(ghost version record)
- 第4位:是否存在空值位圖(Null bitmap ),在SQL Server 2008裏沒有不爲空的列也會有空值位圖(Null bitmap );
- 第5位:表示是否存在變長列;
- 第6位:表示該列包含版本信息;
- 第7位:在SQL Server裏未使用;
用作狀態位2的第2字節(8位)。只有1位用來表示這條記錄是否爲鬼影轉發記錄(ghost forwarded record)。
由行頭開始到定長列結尾長度(定長字段長度):
下2個字節用來表示從記錄開始到定長字段結束的總長度。即所有定長字段長度+4bytes系統數據(行頭2Bytes和定長字段長度記錄2Bytes)。例如如果表裏沒有定長列,這個列的值會是4。這和頁頭列pminlen顯示的值是一樣的。
所有定長列字段值(Fixed_Data_Size):
下n個字節用來存儲在表中的定長數據,n就是在表中所有定長列的長度。如果表裏的所有列都是變長列,這一部分就沒有。
字段個數:
下2個字節用來存儲表裏的列數。
空值位圖(Null_Bitmap):
下n個字節用作空值位圖,每列對應一個bit,1表示對應列爲空。n的值爲:列數 / 8,取整。
Variable_Data_Size:
下2個字節用來存儲表裏變長列個數。
每個變長字段在記錄中的偏移量:
下n個字節用來存儲變長字段值在記錄中的實際偏移量。每個變長列需要2字節,n的值爲:變長列數 * 2 。
變長字段值:
最後n個字節用來存儲所有變長列值,n的值爲所有變長列的實際長度之和。
我們來看一個具體的例子:
創建測試表,並插入2條記錄
CREATE TABLE Customers
(
FirstName CHAR(50) NOT NULL,
LastName CHAR(50) NOT NULL,
Address CHAR(100) NOT NULL,
ZipCode CHAR(5) NOT NULL,
Rating INT NOT NULL,
ModifiedDate DATETIME NOT NULL,
)
GO
INSERT INTO dbo.Customers
( FirstName ,
LastName ,
Address ,
ZipCode ,
Rating ,
ModifiedDate
)
VALUES ( 'Woody' , -- FirstName - char(50)
'Tu' , -- LastName - char(50)
'ZUOQIAO YOUXI TOWN LINHAI CITY' , -- Address - char(50)
'0000' , -- ZipCode - char(5)
1 , -- Rating - int
'2015-05-07 10:09:51' -- ModifiedDate - datetime
)
go
使用DBCC IND命令查看錶對應頁列表:
DBCC IND('InternalStorageFormat','Customers',-1)
我們看到數據頁號爲79
使用DBCC PAGE命令查看頁信息:
DBCC TRACEON(3604)
DBCC PAGE(InternalStorageFormat,1,79,3)
GO
在頁頭pminlen的值是221,包括定長列的總長217 bytes(50+50+100+5+4+8),2 bytes用作狀態位(行頭系統開銷),2 byte 用作由行頭開始到定長列結尾長度。
在記錄槽提到的長度224,包括頁頭pminlen的值,1 byte用作空值位圖(6/8 取整爲1)和2 bytes 的字段個數。
我們來看一個變長列的表。
創建表並插入數據後,查看錶對應的頁:
CREATE TABLE VariableLength(
Title CHAR(10) NOT NULL,
FirstName VARCHAR(100),
Lastname VARCHAR(100),
email VARCHAR(50),
dob date NOT NULL,
phone CHAR(10),
Countrycode CHAR(3),
Designation VARCHAR(100),
PersonalPreference VARCHAR(100)
)
GO
INSERT INTO VariableLength VALUES ('Mr','Woody','Tu','[email protected]','2015-5-7','XXXXXXXXXX','Chn','DBA','Nothing Spl')
GO
DBCC IND('InternalStorageFormat','VariableLength',-1)
我們看到數據頁號爲202。
使用DBCC PAGE命令查看頁信息:
DBCC TRACEON(3604)
GO
DBCC PAGE('InternalStorageFormat',1,202,3)--記得根據你的實際數據庫,修改頁號202
pminlen值爲30,包含:
- 1 byte 狀態位1
- 1 byte 狀態爲2
- 2 bytes 存儲行頭開始到定長列結尾長度
- 26 bytes 所有定長列總長度(10+3+10+3:tittle,dob,phone,countrycode)
- Title CHAR(10) NOT NULL
- dob date NOT NULL
- phone CHAR(10)
- Countrycode CHAR(3)
可以用下列語句驗證下定長列總長度:
SELECT DATALENGTH(Title) title,DATALENGTH(dob) dob,DATALENGTH(phone) phone,DATALENGTH(Countrycode) countrycode FROM VariableLength
在槽0顯示的81長度包含:
- 1 byte 狀態位1
- 1 byte 狀態爲2
- 2 bytes 存儲行頭開始到定長列結尾長度
- 26 bytes 所有定長列總長度(10+3+10+3:tittle,dob,phone,countrycode)
- Title CHAR(10) NOT NULL
- dob date NOT NULL
- phone CHAR(10)
- Countrycode CHAR(3)
- 2 bytes 存儲列個數
- 2 bytes 用作空值位圖,字段個數/8後取整,即 9/8 得到2
- 2 bytes 存儲變長列個數
- 10 bytes 用來存儲每個變長列結束位置的偏移量 變長列個數 * 2,即 5 * 2 得到10,5個變長列包含:
- FirstName VARCHAR(100)
- Lastname VARCHAR(100)
- email VARCHAR(50)
- Designation VARCHAR(100)
- PersonalPreference VARCHAR(100)
- 35 bytes 用來存儲所有變長列的實際長度,這個可以使用下列語句得到
SELECT DATALENGTH(FirstName)+DATALENGTH(Lastname)+DATALENGTH(email)+
DATALENGTH(Designation)+DATALENGTH(PersonalPreference) FROM VariableLength
總結下每條記錄的系統行開銷:
行頭系統數據(2 bytes)+由行頭開始到定長列結尾長度(2 bytes)+列個數(2 bytes)+空值位圖數據(取整(列個數/8) n bytes)
即 2 bytes + 2 bytes + 2 bytes + 取整(列個數/8)
當列個數小於等於8時,系統行開銷始終是7 bytes,往上沒增加8列,增加1 bytes,即系統行開銷始終大於等於7 bytes。
對於在SQL Server裏數據記錄的存儲格式,希望你已經有了清晰的認識。