1. 程式人生 > >Data Pages and Data Rows

Data Pages and Data Rows

資料頁和資料行

資料庫中的空間被劃分為邏輯8KB頁。這些頁以零開始連續編號,並且可以通過指定檔案ID和頁號來引用它們。頁面編號總是連續的,這樣當SQL Server增長資料庫檔案時,從檔案中的最高頁面編號加1開始對新頁面進行編號。類似地,當SQL Server收縮檔案時,它將從檔案中刪除最高數量的頁面。

一般來說,SQL Server在資料庫中儲存和使用資料有三種不同的方法或技術。對於傳統的基於行的儲存,資料儲存在將來自所有列的資料組合在一起的資料行中。

 

SQL Server 2012引入了CurnSt店索引和基於列的儲存。該技術將資料儲存在每列而不是每行的基礎上。我們將在本書第七部分中討論基於列的儲存。

 

最後,在SQL Server 2014中引入了一組記憶體技術,並在SQL Server 2016中進行了進一步改進。即使它們為了冗餘的目的將資料儲存在磁碟上,但是它們的儲存格式與基於行和列的儲存都非常不同。我們將在本書第八部分中討論記憶體技術。

 

本書的這個部分主要關注基於行的儲存和經典的B-Tree索引和堆。

     下圖顯示了資料頁的結構。

96位元組的頁頭包含關於頁的各種資訊,例如頁所屬的物件、頁上可用的行數和空閒空間量、如果頁處於索引頁鏈中的話,則連結到前一頁和下一頁,等等。

 

頁頁首之後是實際資料儲存的區域。其次是自由空間。最後,有一個時隙陣列,它是一個由兩位元組條目組成的塊,指示頁面上相應資料行開始的偏移量。

 

槽陣列指示頁上的資料行的邏輯順序。如果頁面上的資料需要按照索引鍵的順序進行排序,SQL Server不會對頁面上的資料行進行物理排序,而是根據索引排序順序填充插槽陣列。插槽0(在圖1-6中最右邊)儲存頁面上具有最低鍵值的資料行的偏移量;插槽1,第二低鍵值;等等。我們將在下一章更深入地討論索引。

 

SQL Server提供了一組豐富的系統資料型別,這些資料型別可以在邏輯上分為兩個不同的組:固定長度和可變長度。固定長度的資料型別,如int、datetime、char等,總是使用相同數量的儲存空間,而不管它們的值如何,即使它是NULL。例如,int列總是使用4個位元組,而nchar(10)列總是使用20個位元組來儲存資訊。

 

相比之下,可變長度資料型別,如varchar、varbinary和其他一些型別,使用儲存資料所需的儲存空間,再加上兩個額外的位元組。例如,nvarchar(4000)列將僅使用12個位元組來儲存五個字元的字串,在大多數情況下,使用兩個位元組來儲存NULL值。在本章後面,我們將討論變長列不使用NULL值的儲存空間的情況。

 

讓我們看看資料行的結構,如下圖所示。

 

行的前兩個位元組,稱為狀態位A和狀態位B,是點陣圖,其中包含有關行的資訊,例如行型別、該行是否已被邏輯刪除(ghosted)以及該行是否具有NULL值、可變長度列和版本化標記。

 

行中的後兩個位元組用於儲存資料的固定長度部分的長度。其次是固定長度的資料本身。

 

在固定長度資料部分之後,有一個空點陣圖,它包括兩個不同的資料元素。前兩個位元組元素是行中的列數。第二個是空點陣圖陣列。這個陣列對於表的每一列使用一個位,而不管它是否為空。

 

空點陣圖總是出現在堆表或叢集索引葉行中的資料行中,即使該表沒有空列。然而,當索引中沒有空列時,空點陣圖既不存在於非葉索引行中,也不存在於非聚集索引的葉級行中。

 

在空點陣圖之後,是行的可變長度資料部分。它以行中兩位元組數量的可變長度列開始,之後是列偏移陣列。SQL Server為行中的每個可變長度列儲存兩個位元組的偏移值,即使值為NULL。其次是資料的實際可變長度部分。最後,在行的末尾有一個可選的14位元組版本標記。此標記在需要行版本控制的操作期間使用,例如聯機索引重建、樂觀隔離級別、觸發器和其他一些操作。

注意,我們將在第6章中討論索引維護,在第9章中討論觸發器,在第21章中討論樂觀隔離級別。

讓我們建立一個表,用一些資料填充它,並檢視實際的行資料。程式碼顯示在清單1-4中。Replicate函式重複作為第一個引數提供的字元10次。

 

清單1-4。資料行格式:表建立

create  table  dbo.DataRows

(

ID  int  not  null,

Col1  varchar(255)  null,

Col2  varchar(255)  null,

Col3  varchar(255)  null

);

Insert  into  dbo.DataRows (ID, Col1, Col3)

Values (1,replicate('a',10),replicate('c',10));

Insert  into  dbo.DataRows(ID, Col2)

values  (2,replicate('b',10));

 

 dbcc  ind

(

'SQLServerInternals'   /*Database  Name*/

,'dbo.DataRows'   /*Table  Name*/

,-1   /*Display  information  for  all  pages  of  all  indexes*/

);

 

沒有文件但眾所周知的DBCC IND命令返回有關表頁分配的資訊。您可以在圖1-8中看到這個命令的輸出。

 

有兩頁屬於該表。第一個頁面,PageType=10,是一種特殊型別的頁面,稱為IAM分配對映。此頁跟蹤屬於特定物件的頁。但是,現在不要關注這一點,因為我們將在本章後面討論分配對映頁面。

 

注意,SQL Server 2012引入了另一個未文件化的資料管理函式(DMF),sys.dm_db_database_page_.ions,它可以用作DBCC IND命令的替換。與DBCC IND相比,該DMF的輸出提供更多的資訊,並且可以與其他系統DMV和/或目錄檢視連線。

 

具有Page(1)的頁面是包含資料行的實際資料頁。PageFID和PagePID列顯示頁面的實際檔案和頁碼。可以使用另一個未文件化的命令DBCC PAGE檢查其內容,如清單1-5所示。

 

清單1-5。資料行格式:DCBC頁面呼叫

 Redirecting DBCC PAGE output to console dbcc traceon(3604);

dbcc  page

(

'SqlServerInternals'  /*DatabaseName*/

,1 /*File ID*/

,214643   /*Page  ID*/

,3   /*Output  mode:  3  -  display  page  header  and  row  details  */

);

清單1-6顯示了與第一資料行對應的DBCC PAGE的輸出。SQLServer以位元組交換順序儲存資料。例如,兩個位元組的值0001將被儲存為0100。

 

清單1-6 第一行的DCBC頁輸出

Slot  0  Offset  0x60  Length  39

Record  Type   =  PRIMARY_RECORD                

Record  Attributes  =  NULL_BITMAP  VARIABLE_COLUMNS

Record   Size  =  39                                                             

Memory  Dump @0x000000000EABA060                                       

0000000000000000: 30000800  01000000  04000403   001d001d   00270061     0................ '.a

0000000000000014: 61616161  61616161  61636363   63636363   636363         aaaaaaaaacccccccccc

Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4 ID = 1

Slot 0 Column 2 Offset 0x13 Length 10 Length (physical) 10 Col1 = aaaaaaaaaa

Slot 0 Column 3 Offset 0x0 Length 0 Length (physical) 0 Col2 = [NULL]

Slot 0 Column 4 Offset 0x1d Length 10 Length (physical) 10 Col3 = cccccccccc

讓我們更詳細地檢視資料行,如下圖所示。

 

如您所見,行以兩個狀態位開始,後面跟著兩個位元組的值0800。這是位元組交換值0008,這是行中列數屬性的偏移量。此偏移量告訴SQLServer,行的固定長度資料部分結束。

 

接下來的四個位元組用於儲存固定長度的資料,在本例中是ID列。之後,有兩個位元組的值,表明資料行有四列,後面跟著一個1位元組的NULL點陣圖。只有四列,點陣圖中的一個位元組就足夠了。它儲存的值為04,二進位制格式為00000×100。它指示行中的第三列包含空值。

 

接下來的兩個位元組儲存行中可變長度列的數量,為3(以位元組交換的順序是0300)。後面跟著一個偏移陣列,其中每兩個位元組儲存可變長度列資料結束的偏移。可以看到,儘管Col2是NULL,但它仍然使用偏移陣列中的插槽。最後,有來自可變長度列的實際資料。

 

現在,讓我們看一下第二個資料行。清單1-7顯示了DCBC頁面輸出,下圖顯示了行資料。

 

清單1-7 第二行的DCBC頁輸出

Slot  1  Offset  0x87  Length  27

Record  Type  =  PRIMARY_RECORD             

Record  Attributes  =  NULL_BITMAP  VARIABLE_COLUMNS

Record  Size  = 27                                                             

Memory  Dump @0x000000000EABA087                                       

0000000000000000: 30000800  02000000  04000a02  0011001b   00626262  0................ bbb

0000000000000014: 62626262  626262                                                             bbbbbbb      

Slot 1 Column 1 Offset 0x4 Length 4 Length (physical) 4 ID = 2

Slot 1 Column 2 Offset 0x0 Length 0 Length (physical) 0 Col1 = [NULL]

Slot 1 Column 3 Offset 0x11 Length 10 Length (physical) 10 Col2 = bbbbbbbbbb

Slot 1 Column 4 Offset 0x0 Length 0 Length (physical) 0 Col3 = [NULL]

第二行中的NULL位圖表示二進位制值00001010,表明Col1和Col3是NULL。即使表有三個可變長度列,行中可變長度列的數量表明偏移陣列中只有兩個列/插槽。SQL Server不維護關於行中尾隨的NULL可變長度列的資訊。

 

提示:通過建立表,可以將通常儲存空值的可變長度列定義為CREATE TABLE語句中的最後一個列,可以減少資料行的大小。這是CREATE TABLE語句中列順序唯一重要的情況。

 

固定長度的資料和內部屬性必須符合單個數據頁上可用的8060位元組。當情況不是這樣時,SQLServer不允許您建立表。例如,清單1-8中的程式碼會產生錯誤。

 

清單1-8。建立具有超過8060位元組的資料行大小的表

create  table  dbo.BadTable

(

Col1  char(4000),

Col2  char(4060)

)

Msg  1701,  Level  16,  State  1,  Line  1

建立或更改表“BadTable”失敗,因為最小行大小是8067,包括7個位元組的內部開銷。這超過了8060位元組允許的最大表行大小。

 

原文摘自:《Pro SQL Server Internals, 2nd edition》的CHAPTER 1 Data Storage Internals中的Data Pages and Data Rows一節(即P8~P14)

原文連結:http://www.allitebooks.com/pro-sql-server-internals-2nd-edition/