1. 程式人生 > >《Pro SQL Server Internals, 2nd edition》節選翻譯

《Pro SQL Server Internals, 2nd edition》節選翻譯

章節1:資料儲存內部構件

資料頁和資料行

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

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

SQL Server 2012引入了

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

最後,SQL Server 2014中引入了記憶體技術,SQL Server 2016中進一步改進了記憶體技術。儘管出於冗餘目的,它們將資料儲存在磁碟上,但它們的儲存格式與基於行和基於列的儲存都有很大不同。我們將在本書的第8部分中討論記憶體技術。本書的這一部分主要討論基於行的儲存和經典的樹索引和堆

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

1-6資料頁結構

 

    一個96位元組的頁首包含資訊頁面的各個部分,如物件頁面所屬的行數和可用空閒空間頁面,連結到前一個和後一個頁面

,如果頁面在一個索引頁鏈,等等。

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

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

        SQL Server提供了一組豐富的系統資料型別,可以在邏輯上分為兩組

:固定長度組和可變長度組。固定長度的資料型別,如intdatetimechar和其他型別,不管它們的值是多少,都使用相同數量的儲存空間,即使是NULL也是如此。例如,int列總是使用4位元組,而nchar(10)列總是使用20位元組儲存資訊。

    相反,可變長度的資料型別,如varcharvarbinary和其他一些型別,使用的儲存空間與儲存資料所需的空間一樣多,外加兩個位元組。例如,nvarchar(4000)列僅使用12位元組儲存5個字元的字串,在大多數情況下,使用2位元組儲存NULL值。我們將在本章後面討論變長列不為NULL值使用儲存空間的情況。

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

1 - 7.資料行結構

 

    行的前兩個位元組,稱為狀態位A和狀態位B,是點陣圖,其中包含關於行的資訊,比如行型別,如果行已經被邏輯刪除(重定向),如果行有NULL值、可變長度列和版本控制標記。

    行中接下來的兩個位元組用於儲存資料的固定長度部分的長度接著固定長度的資料本身。

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

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

    在null點陣圖之後,是行中可變長度的資料部分。它從一個2位元組開始行中可變長度的列數,然後是列偏移量陣列。SQL Server為行中每個變長列儲存一個2個位元組的偏移量值,即使該值為NULL。其次資料的實際可變長度部分。最後,最後還有一個可選的14位元組版本控制標記的行。此標記用於需要行版本控制的操作,例如線上索引重建,樂觀隔離級別、觸發器等。

 


 

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


 

 讓我們建立一個表,用一些資料填充它,並檢視實際的行資料。程式碼顯示在清單1 - 4複製函式將第一個引數提供的字元重複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中看到該命令的輸出。

 

1 - 8.DBCC IND輸出

 

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

 


 

注意  dm_db_database_page_assignments可以用作DBCC IND命令的替代。DBCC IND相比,此DMF的輸出提供了更多資訊,並且可以與其他系統dmv/或目錄檢視連線。

 

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

清單1 - 5.資料行格式:DBCC頁呼叫

——DBCC頁面輸出重定向到控制檯

dbcc traceon(3604);

dbcc page

(

 'SqlServerInternals' /*Database Name*/

 ,1 /*File ID*/

 ,214643 /*Page ID*/

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

);

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

清單1 - 6.第一行的DBCC頁面輸出

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

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

 

1 - 9.第一個資料行

 

    正如所看到的,行以兩個狀態位開始,然後是兩個位元組的值0800。這是位元組交換值0008,它是行中列數屬性的偏移量。這個偏移量告訴SQL Server行中固定長度的資料部分在哪裡結束。

    接下來的4個位元組用於儲存固定長度的資料,在我們的示例中是ID列。然後是兩個位元組的值,該值顯示資料行有四列,然後是一個一個位元組的空點陣圖。如果只有四列,點陣圖中的一個位元組就足夠了。它以二進位制格式儲存04的值,即00000100。它表示行中的第三列包含空值。

    接下來的兩個位元組儲存行中可變長度列的數量,即3(位元組順序為0300)。它後面是一個偏移量陣列,其中每兩個位元組儲存變數列資料結束的偏移量。如所見,即使Col2NULL,它仍然使用偏移陣列中的插槽。最後,還有來自可變長度列的實際資料。

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

 

1 - 10.第二資料行資料

清單1 - 7.DBCC頁輸出為第二行

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]

       <br>第二行中的NULL位圖表示二進位制值00001010,表示Col1<br><div class="br-height"></div>Col3NULL。即使表有三個可變長列,行中可變長列的數量表明偏移陣列中只有兩列/槽。SQL Server不維護行中尾隨的空變長列的資訊。

 

 


 

□建議 可以通過建立表來減少資料行的大小,方法是將通常儲存null值的變長列定義為CREATE TABLE語句中的最後一列。這是CREATE TABLE語句中列的順序惟一重要的情況。


 

  <br>固定長度的資料和內部屬性必須適合單個數據頁上可用的8060位元組。<br><div class="br-height"></div>如果不是這樣,SQL Server不允許建立表。例如,清單1-8中的程式碼產生了一個錯誤。

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

create table dbo.BadTable

(

 Col1 char(4000),

 Col2 char(4060)

)

Msg 1701, Level 16, State 1, Line 1

<br>建立或更改表“BadTable”失敗,因為最小行大小為8067,包括7位元組的內部開銷。這超過了錶行大小的最大允許值<br><div class="br-height"></div>8060位元組。

 

本翻譯節選《Pro SQL Server Internals, 2nd edition》中的一小節,原文可自行查詢瀏覽。