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個字節用來存儲行頭開始到定長列結尾長度。它包含2個狀態位,2個字節用作這個列表示在表中定長數據的實際長度。例如如果表里沒有定長列,這個列的值會是4。這和頁頭列pminlen顯示的值是一樣的。

所有定長列字段值(Fixed_Data_Size):

下n個字節用來存儲在表中的定長數據,n就是在表中所有定長列的長度。如果表里的所有列都是變長列,這一部分就沒有。

空值位圖(Null_Bitmap):

下2個字節用來存儲表里的列數。

下n個字節用作空值位圖,每個bit對應一個列,1表示對應列為空。n的值為:列數 / 8,將值取整。

Variable_Data_Size:

下2個字節用來存儲表里變長列個數。

下n個字節用來存儲每個變長列結束為止的偏移量。每個變長列需要2字節,n的值為:變長列數 * 2 。

最后n個字節用來存儲所有變長列值,n的值為所有變長列的實際長度的總長度。

 我們來看一個具體的例子:

創建數據庫,並插入2條記錄

 1 USE [InternalStorageFormat]
 2 GO
 3 
 4 IF EXISTS ( SELECT  *
 5             FROM    sysobjects
 6             WHERE   id = OBJECT_ID(N'[dbo].[Customers]')
 7                     AND OBJECTPROPERTY(id, N'IsUserTable') = 1 )
 8     DROP TABLE dbo.Customers
 9 
10 CREATE TABLE Customers
11 (
12    FirstName CHAR(50) NOT NULL,
13    LastName CHAR(50) NOT NULL,
14    Address CHAR(100) NOT NULL,
15    ZipCode CHAR(5) NOT NULL,
16    Rating INT NOT NULL,
17    ModifiedDate DATETIME NOT NULL,
18 )
19 GO
20 
21 
22 INSERT INTO dbo.Customers
23         ( FirstName ,
24           LastName ,
25           Address ,
26           ZipCode ,
27           Rating ,
28           ModifiedDate
29         )
30 VALUES  ( 'Woody' , -- FirstName - char(50)
31           'Tu' , -- LastName - char(50)
32           'ZUOQIAO YOUXI TOWN LINHAI CITY' , -- Address - char(50)
33           '0000' , -- ZipCode - char(5)
34           1 , -- Rating - int
35           '2015-05-07 10:09:51'  -- ModifiedDate - datetime
36         )
37         go 2

使用DBCC IND命令查看表對應頁列表:

1 DBCC IND('InternalStorageFormat','Customers',-1)

我們看到數據頁號為79。

使用DBCC PAGE命令查看頁信息:

1 DBCC TRACEON(3604)
2 DBCC PAGE(InternalStorageFormat,1,79,3)
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 的字段個數。

我們來看一個變長列的表。

創建表並插入數據后,查看表對應的頁:

 1 CREATE TABLE VariableLength(
 2    Title         CHAR(10) NOT NULL,
 3    FirstName     VARCHAR(100),
 4    Lastname      VARCHAR(100),
 5    email         VARCHAR(50), 
 6    dob           date NOT NULL,
 7    phone         CHAR(10),
 8    Countrycode   CHAR(3),
 9    Designation   VARCHAR(100),
10    PersonalPreference VARCHAR(100)
11 )
12 GO
13 INSERT INTO VariableLength VALUES ('Mr','Woody','Tu','smartgz@qq.com','2015-5-7','XXXXXXXXXX','Chn','DBA','Nothing Spl')
14 GO
15 DBCC IND('InternalStorageFormat','VariableLength',-1)

我們看到數據頁號為202。

使用DBCC PAGE命令查看頁信息:

1 DBCC TRACEON(3604)
2 GO
3 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)

可以用下列語句驗證下定長列總長度: 

1 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 用來存儲所有變長列的實際長度,這個可以使用下列語句得到
1 SELECT DATALENGTH(FirstName)+DATALENGTH(Lastname)+DATALENGTH(email)+
2 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里數據記錄的存儲格式,希望你已經有了清晰的認識。

 參考文章:

http://www.sqlservercentral.com/blogs/practicalsqldba/2012/08/22/sql-serverunderstanding-the-data-record-structure/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM