SQL Server 堆錶行存儲大小(Record Size)

一.本文所涉及的內容(Contents)

  1. 本文所涉及的內容(Contents)
  2. 背景(Contexts)
  3. 堆錶行記錄存儲格式(Heap)
  4. 案例分析(Case)
  5. 參考文獻(References)

二.背景(Contexts)

  有的時候你須要計算堆表的一行記錄有多大?又或者想計算一個數據頁(8K)能保存多少條記錄?字段類型是設計成nchar仍是nvarchar?他們有什麼區別呢?在作數據庫表設計的時候會常常出現這些問題。要計算一行記錄的大小,並非簡單把列字段類型大小直接相加就行的,具體緣由請看下文。html

三.堆錶行記錄存儲格式(Heap)

  下面是計算堆錶行記錄大小的公式,它引自MSDN:估計堆的大小sql

計算公式:Row_Size = Fixed_Data_Size + Variable_Data_Size + Null_Bitmap + 4數據庫

= 全部固定長度列的總字節大小 + 可變長度數據的大小 + Null位圖 + 數據行的行標題開銷sqlserver

  單看上面的計算公式是比較難理解的,並且MSDN並無提供相關的說明,好比上面公式最後的4表明什麼意思?我參考了一些書籍以後整理出下面更容易理解的堆記錄存儲格式圖,但願能幫助你們理解:測試

1_堆記錄格式

(Figure1:堆表記錄存儲格式)編碼

四.案例分析(Case)

  本文針對堆表數據頁的存儲作一個測試,測試不一樣數據類型的存儲大小,測試的數據類型包括:int、char、nchar和nvarchar,測試後你會理解什麼是定長類型、什麼是變長類型,他們在存儲上有什麼區別;spa

(一) 首先建立一個測試數據庫;.net

/******* Step1:建立示例數據庫*******/
USE master
GO
IF EXISTS(SELECT name FROM sys.databases WHERE name = 'RecordSize_DB')
DROP DATABASE RecordSize_DB
GO
CREATE DATABASE RecordSize_DB
GO

 

(二) 接着在數據庫中建立3個不一樣的堆表:[HeapPage_char]、[HeapPage_nchar] 和[HeapPage_nvarchar],3個表分別表明:char、nchar和nvarchar不一樣數據類型的存儲;設計

USE RecordSize_DB
GO
/******* Step2:建立個堆表*******/
CREATE TABLE [dbo].[HeapPage_char](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [names] [char](10) NULL
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[HeapPage_nchar](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [names] [nchar](10) NULL
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[HeapPage_nvarchar](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [names] [nvarchar](10) NULL
) ON [PRIMARY]
GO

 

(三) 再接着就是在3個不一樣的堆表插入相同的數據;3d

/******* Step3:分別插入測試數據*******/
INSERT INTO [HeapPage_char](names) values('XX')
GO
INSERT INTO [HeapPage_char](names) values('XXXX')
GO 2

INSERT INTO [HeapPage_nchar](names) values('XX')
GO
INSERT INTO [HeapPage_nchar](names) values('XXXX')
GO 2

INSERT INTO [HeapPage_nvarchar](names) values('XX')
GO
INSERT INTO [HeapPage_nvarchar](names) values('XXXX')
GO 2

 

(四) 返回3個測試表數據;

-- 返回表數據
SELECT * FROM [HeapPage_char]
SELECT * FROM [HeapPage_nchar]
SELECT * FROM [HeapPage_nvarchar]

wps_clip_image-6897

(Figure2:HeapPage_char)

wps_clip_image-20078

(Figure3:HeapPage_nchar)

wps_clip_image-7225

(Figure4:HeapPage_nvarchar)

 

(五) 經過開啓3604跟蹤和DBCC PAGE命令查詢數據的存儲信息;

/******* Step4:查看各表數據頁大小*******/
--開啓跟蹤
DBCC TRACEON(3604)
--查看錶信息
DBCC IND(RecordSize_DB,HeapPage_char,-1)

上面的命令會返回以下圖所示的信息:

wps_clip_image-19265

(Figure5:堆表HeapPage_char)

上圖返回結果都會由於每次建立而有所不一樣,你須要根據每次不一樣的值來填寫下面的DBCC命令中的參數。上圖第一行記錄爲IAM page,第二行爲data page(數據頁),這裏是查看數據頁的內容,在參數中填入PageFID=1和PagePID=80

--查看PAGE信息
DBCC PAGE(RecordSize_DB,1,80,1)

執行上面的DBCC PAGE命令將返回下面信息:

DATA:
Slot 0, Offset 0x60, Length 21, DumpStyle BYTE

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP     Record Size = 21

Memory Dump @0x6523C060

00000000:   10001200 01000000 58582020 20202020 †........XX               
00000010:   20200200 00††††††††††††††††††††††††††  ...                    

Slot 1, Offset 0x75, Length 21, DumpStyle BYTE

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP     Record Size = 21

Memory Dump @0x6523C075

00000000:   10001200 02000000 58585858 20202020 †........XXXX             
00000010:   20200200 00††††††††††††††††††††††††††  ...                    

Slot 2, Offset 0x8a, Length 21, DumpStyle BYTE

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP     Record Size = 21

Memory Dump @0x6523C08A

00000000:   10001200 03000000 58585858 20202020 †........XXXX             
00000010:   20200200 00††††††††††††††††††††††††††  ...                    

OFFSET TABLE:
Row - Offset                         
2 (0x2) - 138 (0x8a) 
1 (0x1) - 117 (0x75) 
0 (0x0) - 96 (0x60)  

從上面的信息咱們能夠知道3條記錄的長度都是:Record Size = 21,這個值是怎麼算出來的呢?公式能夠參考:估計堆的大小

計算公式:Row_Size = Fixed_Data_Size + Variable_Data_Size + Null_Bitmap + 4

= 全部固定長度列的總字節大小 + 可變長度數據的大小 + Null位圖 + 數據行的行標題開銷

計算過程:

Fixed_Data_Size = 全部固定長度列的總字節大小 = 4+10(int大小爲4個字節,char(10)固定大小爲10個字節)

Variable_Data_Size = 2 + (Num_Variable_Cols x 2) + Max_Var_Size

  Num_Variable_Cols = 可變長度列數 = 0(沒有可變長度列)

  Max_Var_Size = 全部可變長度列的最大總字節大小 = 0

Variable_Data_Size = 0(若是沒有可變長度列,請將 Variable_Data_Size 設置爲 0)

Null_Bitmap = 2 + ((Num_Cols + 7) / 8)

  Num_Cols = 總列數(固定長度和可變長度)= 2(id、names兩個列)

  Null_Bitmap = 3(只保留整數部分,放棄全部餘數)

Row_Size = Fixed_Data_Size + Variable_Data_Size + Null_Bitmap + 4

  = 14 + 0 + 3 +4 = 21(與上面Record Size = 21符合)

 

(六) 接下來,咱們來看看HeapPage_nchar表的存儲大小會有什麼不一樣:

--查看錶HeapPage_nchar
DBCC ind(RecordSize_DB,HeapPage_nchar,-1)

wps_clip_image-16737

(Figure6:堆表HeapPage_nchar)

--查看PAGE信息
DBCC PAGE(RecordSize_DB,1,90,1)
DATA:
Slot 0, Offset 0x60, Length 31, DumpStyle BYTE

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP     Record Size = 31

Memory Dump @0x61ADC060

00000000:   10001c00 01000000 58005800 20002000 †........X.X. . .         
00000010:   20002000 20002000 20002000 020000†††† . . . . . ....          

Slot 1, Offset 0x7f, Length 31, DumpStyle BYTE

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP     Record Size = 31

Memory Dump @0x61ADC07F

00000000:   10001c00 02000000 58005800 58005800 †........X.X.X.X.         
00000010:   20002000 20002000 20002000 020000†††† . . . . . ....          

Slot 2, Offset 0x9e, Length 31, DumpStyle BYTE

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP     Record Size = 31

Memory Dump @0x61ADC09E

00000000:   10001c00 03000000 58005800 58005800 †........X.X.X.X.         
00000010:   20002000 20002000 20002000 020000†††† . . . . . ....          

OFFSET TABLE:

Row - Offset                         
2 (0x2) - 158 (0x9e) 
1 (0x1) - 127 (0x7f) 
0 (0x0) - 96 (0x60)  

套用上面的公式:

計算公式:Row_Size = Fixed_Data_Size + Variable_Data_Size + Null_Bitmap + 4

= 全部固定長度列的總字節大小 + 可變長度數據的大小 + Null位圖 + 數據行的行標題開銷

計算過程:

Fixed_Data_Size = 全部固定長度列的總字節大小 = 4+10*2(int大小爲4個字節,nchar(10)固定大小爲20個字節

Variable_Data_Size = 2 + (Num_Variable_Cols x 2) + Max_Var_Size

  Num_Variable_Cols = 可變長度列數 = 0(沒有可變長度列)

  Max_Var_Size = 全部可變長度列的最大總字節大小 = 0

Variable_Data_Size = 0(若是沒有可變長度列,請將 Variable_Data_Size 設置爲 0)

Null_Bitmap = 2 + ((Num_Cols + 7) / 8)

  Num_Cols = 總列數(固定長度和可變長度)= 2(id、names兩個列)

  Null_Bitmap = 3(只保留整數部分,放棄全部餘數)

Row_Size = Fixed_Data_Size + Variable_Data_Size + Null_Bitmap + 4

  = 24 + 0 + 3 +4 = 31(與上面Record Size = 31符合)

 

  總結:從上面測試結果來看:char(10)與nchar(10)是固定長度數據類型,不管你保存多少內容,在數據頁中會爲記錄保留固定長度的空間,而char(10)與nchar(10)的區別是nchar(10)佔用的空間是char(10)的兩倍;

 

(七) 接下來,咱們來看看HeapPage_nvarchar表的存儲大小又有什麼不一樣:

--查看錶HeapPage_nvarchar
DBCC ind(RecordSize_DB,HeapPage_nvarchar,-1)

wps_clip_image-12300

(Figure7:堆表HeapPage_nvarchar)

--查看PAGE信息
DBCC PAGE(RecordSize_DB,1,94,1)
DATA:
Slot 0, Offset 0x60, Length 19, DumpStyle BYTE

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS
Record Size = 19                     
Memory Dump @0x61ADC060

00000000:   30000800 01000000 02000001 00130058 †0..............X         
00000010:   005800†††††††††††††††††††††††††††††††.X.                      

Slot 1, Offset 0x73, Length 23, DumpStyle BYTE

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS
Record Size = 23                     
Memory Dump @0x61ADC073

00000000:   30000800 02000000 02000001 00170058 †0..............X         
00000010:   00580058 005800††††††††††††††††††††††.X.X.X.                  

Slot 2, Offset 0x8a, Length 23, DumpStyle BYTE

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS
Record Size = 23                     
Memory Dump @0x61ADC08A

00000000:   30000800 03000000 02000001 00170058 †0..............X         
00000010:   00580058 005800††††††††††††††††††††††.X.X.X.                  

OFFSET TABLE:

Row - Offset                         
2 (0x2) - 138 (0x8a)  
1 (0x1) - 115 (0x73)  
0 (0x0) - 96 (0x60)      

  

套用上面的公式:

計算公式:Row_Size = Fixed_Data_Size + Variable_Data_Size + Null_Bitmap + 4

= 全部固定長度列的總字節大小 + 可變長度數據的大小 + Null位圖 + 數據行的行標題開銷

計算過程:

Fixed_Data_Size = 全部固定長度列的總字節大小 = 4(int大小爲4個字節)

Variable_Data_Size = 2 + (Num_Variable_Cols x 2) + Max_Var_Size

  Num_Variable_Cols = 可變長度列數 = 1(names列的數據類型是nvarchar)

  Max_Var_Size = 全部可變長度列的最大總字節大小 = 第一行記錄是2*2;第2、三行記錄是2*4

  第一行記錄Variable_Data_Size = 2 + (1 * 2) + (2*2) = 8

  第2、三行記錄Variable_Data_Size = 2 + (1 * 2) + (2*4) = 12

Null_Bitmap = 2 + ((Num_Cols + 7) / 8)

  Num_Cols = 總列數(固定長度和可變長度)= 2(id、names兩個列)

  Null_Bitmap = 2 + ((2 + 7) / 8) = 3(只保留整數部分,放棄全部餘數)

Row_Size = Fixed_Data_Size + Variable_Data_Size + Null_Bitmap + 4

  第一行記錄Row_Size = 4 + 8 + 3 + 4 = 19(與上面第一行記錄Record Size = 19符合)

  第2、三行記錄Row_Size = 4 + 12 + 3 + 4 = 23(與上面第2、三行記錄Record Size = 23符合)

 

  總結:從上面結果來看:nvarchar(10)是變長度數據類型,你輸入字符串有多少就存儲多少內容,這就是定長數據類型與變長數據類型的區別,從第一行記錄(佔用19 Bytes)與第2、三行記錄(佔用23 Bytes)佔用了不一樣的數據空間能夠證明這一點;而nchar(10)與nvarchar(10)同樣,都屬於Unicode編碼,因此佔用的空間是非Unicode編碼的2倍;

五.參考文獻(References)

估計堆的大小

估計數據庫的大小

SQL SERVER單頁數據存儲行數計算

SQL Server頁中行物理存儲

SQL Server計算數據庫中表、堆、彙集索引和非彙集索引的大小

SQL Server 2008連載之存儲結構

Stairway to SQL Server Indexes(中文:SQL Server索引進階

相關文章
相關標籤/搜索