
目錄
什麼時候觸發行溢出
行溢出的存儲結構
行溢出的整體邏輯結構圖
總結
本文以sql server為例聊聊行溢出的存儲結構。
1什麼時候觸發行溢出
sql server的行溢出資料隻會發生在變長字段上,變長列的長度不能超過标準變長列最大值8000個位元組的限制,而且還要滿足:
包括行頭系統資訊和所有定長列和變長系統資訊的所有長度不能超過8060位元組,要想存儲8000位元組以上的資料,應該使用lob(text、ntext或者image)或者max資料類型;
變長列的實際長度一定要超過24個位元組(因為行溢出需要額外的24個位元組行溢出指針,如果變長字段值不超過24個位元組,完全沒有必要把它作為行溢出資料存儲);
變長列不能是聚集索引鍵的一部分(如果行溢出是聚集索引鍵的一部分,那麼表的查詢性能會是一個噩夢);
2行溢出的存儲結構
為了了解overflow的結構,我們建立表heappage_overflow,并插入測試資料:
檢視這行記錄對于page的内容:
上面page資料中的一行記錄可以格式化為下圖所示:
圖1:堆表heappage_overflow記錄結構
其中幾個重要部分結構解釋如下:
0x2980這是包含後面指向存儲行溢出的變長字段偏移量。把0x2980逆序成0x8029,再把0x8029轉換為二進制1000000000101001,去除高2位(也就是粗體部分),取101001,轉換為十進制就是41,高2位的目的其實隻是一個辨別,為了跟普通記錄的變長字段偏移量進行區分。
第一個變長字段偏移量41是由17個位元組系統資訊加上24個位元組的行溢出指針共同組成,計算公式為:41=17+24,下面對這24個位元組的行溢出指針進行結構分析:
圖2:堆表heappage_overflow的行溢出指針結構
0x02,特殊字段的類型,0x02表示行溢出資料;
0x0000,表示b樹中的層級,行溢出的記錄,這個值為始終為0,在lob記錄的root記錄中這個值為0x0100;
0x00,暫時未使用;
0x01000000,一個序列号,每次行溢出或lob資料被更新時這個值加1,并在樂觀并發控制為遊标使用;
0x804a0000,timestamp值,用于使用dbcc checktable檢查表索引、行内、lob 以及行溢出資料頁是否已正确連結。在lob的行内資料、lob的root指針以及存儲lob的資料結構中都存儲了這個值,而且他們的值都是一樣的。0x804a0000逆序之後是0x0004a80,再向0x0004a80後面追加4個0得到0x0004a800000,轉換為十進制為1249902592;要驗證這個辨別值的算法,可以使用工具winhex修改0x804a0000值并使用dbcc page(overflow,1,93,3) 檢視timestamp值。
0x70170000,溢出字段長度,0x70170000逆序之後是0x00001770,用十進制表示是6000,這個跟前面插入記錄時字段的大小完全吻合;
0x5900000001000000表示一個8個位元組的rid位址,指向行溢出字段varcol存儲6000位元組所在資料頁的rid位址為:(1:89:0)。
上面已經分析了在行内資料中存儲的行溢出指針的結構,接下來将分析存儲行溢出資料的實體結構。
檢視行溢出存儲page的内容:
圖3:堆表heappage_overflow的行溢出資料結構
0x0800是這一行記錄的行頭資料,分解為byte#0的十六進制是0x08和byte#1的十六進制是0x00,0x08轉換為二進制是:00001000,各個bit表示的含義如下:
圖4:堆表heappage_overflow的行溢出行頭結構
0x7e17是變長偏移量,經過逆序之後是0x177e,用十進制表示是6014,這個偏移量包含了14個位元組的行溢出系統資料和6000個位元組的行溢出字段值;
0x0000804a00000000是blob id值,跟in_row_data記錄中24個位元組的行溢出指針的timestamp值是相等的。要驗證這個辨別值的算法,可以使用工具winhex修改0x0000804a00000000的值并使用dbcc page(overflow,1,89,3)檢視這個blob id值。如果不相等,雖然select一樣能查詢資料,但是在進行dbcc checkdb将會報引用不比對的錯誤資訊。
0x0300是資料類型,轉換為十進制是3,type=3表示data,即表示這行記錄是用于存儲資料的。
3行溢出的整體邏輯結構圖
根據上面對行記錄存儲結構的分析,行溢出的邏輯可以通過下面的圖來表示:
圖5:堆表heappage_forward第一行記錄行溢出後的存儲結構示意圖
4總結
上文以堆表的行溢出資料為例講解它的存儲結構,從結構來看一行記錄的存儲跨越了兩個page,相比于一條記錄存儲在一個page裡,查詢的時候增加了1個io,當表比較大的時候,随機io将會猛增,将會出現性能上的問題,一般建議控制好變長字段的大小,或者使用其它資料類型避免行溢出,也可以考慮表的垂直拆分。
更多關于sql server存儲結構請參考《sql server性能調優實戰》
作者介紹:陳暢亮
【dba+社群】廣州聯合發起人
微軟sql server方向最有價值專家(mvp),《sql server性能調優實戰》作者,《windows powershell實戰指南(第2版)》譯者。
主要研究mysql、sql server、nosql,以及分布式環境下海量資料存儲的設計與開發。
2015年dtcc大會演講嘉賓
<b></b>
<b>本文來自雲栖社群合作夥伴"dbaplus",原文釋出時間:2015-12-23</b>