1. 程式人生 > >《Pro SQL Server Internals, 2nd edition》的CHAPTER 1 Data Storage Internals中的Data Pages and Data Rows(翻譯)

《Pro SQL Server Internals, 2nd edition》的CHAPTER 1 Data Storage Internals中的Data Pages and Data Rows(翻譯)

資料頁和資料行

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

SQL SERVER中資料儲存

一般來說,有三種不同的方法或技術,SQL Server儲存和處理資料庫中的資料。與經典的基於行的儲存,資料儲存在資料行,將所有列的資料結合在一起。

SQL Server 2012介紹columnstore索引和基於列儲存。這種技術儲存在每列的資料而不是行。第七部分中我們將討論基於列儲存的這本書。

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

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

圖1-6顯示了資料頁的結構。

 

 

1-6資料頁結構

 

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值的儲存空間的情況。

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

 

1-7。資料行結構

行的前兩個位元組,稱為狀態位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' /*資料庫名稱*/

    ,'dbo.DataRows' /*表名*/

    ,--1 /*顯示所有索引的所有頁的資訊*/

);


沒有文件但眾所周知的DBCC IND命令返回有關表頁的資訊

分配。您可以在圖1-8中看到這個命令的輸出。

 

1-8。資料庫輸出

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

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

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

列表1-5。資料行格式:DCBC頁面呼叫

--將DCBC頁面輸出重定向到控制檯

dbcc traceon(3604);

dbcc page

(

    'SqlServerInternals' /*資料庫名稱*/

    ,1 /*File ID*/

    ,214643 /*Page ID*/

    ,3 /*輸出模式: 3 – 顯示頁首和行細節*/

); 

列表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

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

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

  

1-9。第一資料行

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

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

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

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

  

1-10。第二資料行資料

列表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

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]

bbbbbbb 

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

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

The fixed-length data and internal attributes must fit into the 8,060 bytes available on the single data page. SQL Server does not let you create the table when this is not the case. For example, the code in Listing 1-8 produces an error. 

列表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位元組。

大物件儲存

即使固定長度資料和行的內部屬性必須適合於單個頁面,SQL Server也可以將可變長度資料儲存在不同的資料頁上。根據資料型別和長度,有兩種不同的方法儲存資料。

行溢位儲存器


SQL Server在稱為行溢位頁的特殊頁上儲存不超過8000位元組的可變長度列資料。讓我們建立一個表並用清單1-9所示的資料填充它。

列表1-9。

行溢位資料:建立表

create table dbo.RowOverflow

(

    ID int not null,

    Col1 varchar(8000) null,

    Col2 varchar(8000) null

); 

insert into dbo.RowOverflow(ID, Col1, Col2) values

(1,replicate('a',8000),replicate('b',8000));

如你所見,SQL Server建立表並插入資料行沒有任何錯誤,即使資料行大小超過8060位元組。讓我們看看使用DCBCC的命令分配表頁。結果如圖1-11所示。

 

圖1-11。行溢位資料:DBCC IND的結果