陈畅亮 - 深入理解SQL Server存储结构

玉雨泽

2018/05/13 发布于 技术 分类

深入理解SQL Server存储结构,讲述SQL Server堆表、聚集表的记录、索引存储结构,当然还有NULL位图、行溢出、LOB大对象的存储结构,从内部原理理解SQL Server性能调优。

文字内容
1. 深入理解SQL Server存储结构 陈畅亮 2015.4
2. 个人简介  DBA  SQL Server MVP  数据库/大数据/自动化运维  联系方式 - 微博:听风吹雨-ccl - 博客:听风吹雨
3. 提纲  几个工具&几个概念(Page/RID)  堆表记录的存储结构  char VS nchar VS nvarchar  非聚集索引的存储结构  聚集索引表的存储结构  行溢出存储结构  LOB存储结构
4. 几个工具 DBCC Database Consloe Commands 数据库控制台命令  DBCC HELP  DBCC IND  DBCC PAGE  Winhex  Internals Viewer for SQL Server
5. MSDN Page 96字节 页头  数据存储的基本单位 10000a00 01000000 61610200 00 10000a00 05000000 62620200 00 行记录 10000a00 04000000 63630200 00  每页行记大录数小据存储为方向 8KB 行偏移量存储方向 109 122 96 空闲空间 行偏移量 个人理解
6. Page结构示意图 96字节 10000a00 01000000 61610200 00 10000a00 05000000 62620200 00 10000a00 04000000 63630200 00 行记录数据存储方向 行偏移量存储方向 109 122 96 页头 行记录 空闲空间 行偏移量  固定大小为96个字节  存储堆表或者索引数据  行记录与行偏移量公用 的存储区域  从右往左称为槽(slot)
7. RID  什么是RID?  RID:Row Identifier(行标识符)  十六进制RID = 页号 + 文件号 + 槽号  8个字节 = 4 + 2 + 2  RID的存储结构?
8. RID长什么样? 01 SELECT ID, %%physloc%% AS RID_16, 02 sys.fn_PhysLocFormatter(%%physloc%%) AS RID_10 03 FROM dbo.MyTableName 04 GO
9. RID结构示例 十六进制的RID:0x4A0F5F0103000100 4A0F5F01 0300 0100 (3:23007050:1) 槽号(1) 文件号(3) 页号(23007050) 以1字节(byte)为一个单位进行逆序
10. RID的页号转换 8个字节的页号:0x4A0F5F01 4A 0F 5F 01 01 5F 0F 4A 逆序 0x4A0F5F01 0x015F0F4A 23007050
11. 堆表记录的存储结构 MSDN
12. 堆表记录的存储结构 4 Fixed_Data_Size Null_Bitmap Variable_Data_Size 2 Bytes 2 Bytes n Bytes 2 Bytes Ceil(字段个数/8) 2 Bytes 变成字段数 x 2 Bytes n Bytes 行头系 统数据 由行头开 始到定长 字段结尾 的长度 所有定长 字段值 字段 个数 空值字段位图数 据,每个bit对应 1个字段,1表示 其对应字段为空 变长字 段个数 每个变长字 段值结束位 置的偏移量 所有变长 字段值
13. char VS nchar VS nvarchar char [ ( n ) ] 固定长度的非 Unicode 字符串数据,n 用于定义字符串 长度,存储大小为 n 字节。 nchar [ ( n ) ] 固定长度的 Unicode 字符串数据,n 用于定义字符串长 度,存储大小为 n 字节的两倍。 nvarchar [ ( n max ) ] 可变长度的 Unicode 字符串数据。 n 用于定义字符串长 度,存储大小(以字节为单位)是所输入数据实际长度的两倍 + 2 个字节。
14. 01 USE master 02 GO SQL脚本 03 IF EXISTS(SELECT name FROM sys.databases WHERE name = 'Heap_Record') 04 DROP DATABASE Heap_Record 05 GO 06 CREATE DATABASE Heap_Record 07 GO 08 09 USE Heap_Record 10 GO 11 CREATE TABLE [dbo].[HeapPage_char]( 12 [id] [int] IDENTITY(1,1) NOT NULL, 13 [names] [char](10) NULL 14 ) ON [PRIMARY] 15 GO 16 17 CREATE TABLE [dbo].[HeapPage_nchar]( 18 [id] [int] IDENTITY(1,1) NOT NULL, 19 [names] [nchar](10) NULL 20 ) ON [PRIMARY] 21 GO 22 23 CREATE TABLE [dbo].[HeapPage_nvarchar]( 24 [id] [int] IDENTITY(1,1) NOT NULL, 25 [names] [nvarchar](10) NULL 26 ) ON [PRIMARY] 27 GO 28 29 INSERT INTO [HeapPage_char](names) values('XX') 30 GO 31 INSERT INTO [HeapPage_char](names) values('XXXX') 32 GO 2 33 34 INSERT INTO [HeapPage_nchar](names) values('XX') 35 GO 36 INSERT INTO [HeapPage_nchar](names) values('XXXX') 37 GO 2 38 39 INSERT INTO [HeapPage_nvarchar](names) values('XX') 40 GO 41 INSERT INTO [HeapPage_nvarchar](names) values('XXXX') 42 GO 2
15. 堆表行记录结构(char) DBCC IND(Heap_Record,HeapPage_char,-1) 01 DBCC TRACEON(3604) 02 DBCC PAGE(Heap_Record,1,80,1)
16. 堆表行记录结构(char) 01 DATA: 02 Slot 0, Offset 0x60, Length 21, DumpStyle BYTE 03 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 21 04 Memory Dump @0x6523C060 05 00000000: 10001200 01000000 58582020 20202020 ?........XX 06 00000010: 20200200 00?????????????????????????? ... 07 08 Slot 1, Offset 0x75, Length 21, DumpStyle BYTE 09 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 21 10 Memory Dump @0x6523C075 11 00000000: 10001200 02000000 58585858 20202020 ?........XXXX 12 00000010: 20200200 00?????????????????????????? ... 13 14 Slot 2, Offset 0x8a, Length 21, DumpStyle BYTE 15 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 21 16 Memory Dump @0x6523C08A 17 00000000: 10001200 03000000 58585858 20202020 ?........XXXX 18 00000010: 20200200 00?????????????????????????? ... 19 20 OFFSET TABLE: 21 Row - Offset 22 2 (0x2) - 138 (0x8a) 23 1 (0x1) - 117 (0x75) 24 0 (0x0) - 96 (0x60)
17. 堆表行记录结构(char) 原始数据 100012000100000058582020202020202020020000 字节数 原始数据 分割 2 2 4 10 2 1 1000 1200 01000000 58582020202020202020 0200 00 行头 定长字段 偏移 量 键值 INT 键值 CHAR 字段 NULL 个数 位图 可读数据 1000 18 分解 1 XX 2+2+4+10 = 18 2+2+4+10+2+1 = 21 2 0
18. 堆表行记录结构(nchar) DBCC IND(Heap_Record,HeapPage_nchar,-1) 01 DBCC TRACEON(3604) 02 DBCC PAGE(Heap_Record,1,90,1)
19. 堆表行记录结构(nchar) 01 DATA: 02 Slot 0, Offset 0x60, Length 31, DumpStyle BYTE 03 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 31 04 Memory Dump @0x61ADC060 05 00000000: 10001c00 01000000 58005800 20002000 ?........X.X. . . 06 00000010: 20002000 20002000 20002000 020000???? . . . . . .... 07 08 Slot 1, Offset 0x7f, Length 31, DumpStyle BYTE 09 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 31 10 Memory Dump @0x61ADC07F 11 00000000: 10001c00 02000000 58005800 58005800 ?........X.X.X.X. 12 00000010: 20002000 20002000 20002000 020000???? . . . . . .... 13 14 Slot 2, Offset 0x9e, Length 31, DumpStyle BYTE 15 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 31 16 Memory Dump @0x61ADC09E 17 00000000: 10001c00 03000000 58005800 58005800 ?........X.X.X.X. 18 00000010: 20002000 20002000 20002000 020000???? . . . . . .... 19 20 OFFSET TABLE: 21 Row - Offset 22 2 (0x2) - 158 (0x9e) 23 1 (0x1) - 127 (0x7f) 24 0 (0x0) - 96 (0x60)
20. 堆表行记录结构(nchar) 原始数据 10001C00010000005800580020002000200020002000200020002000020000 字节数 原始数据 分割 2 2 4 20 2 1 1000 1C00 01000000 5800580020002000200020002000200020002000 0200 00 行头 定长字段 偏移量 键值 INT 键值 NCHAR 字段 NULL 个数 位图 可读数据 1000 28 分解 1 XX 2+2+4+20 = 28 2+2+4+20+2+1 = 31 2 0
21. 堆表行记录结构(nvarchar) 原始数据 30000800010000000200000100170058005800 字节数 原始数据 分割 2 2 4 2 1 2 2 4 3000 0800 01000000 0200 00 0100 1700 58005800 行头 定长 字段 偏移 量 键值 INT 字段 NULL 变长字 变长字段 键值 个数 位图 段个数 偏移量 NVARCHAR 可读数据 3000 8 1 2 0 1 23 XX 分解 2+2+4 = 8 2+2+4+2+1+2+2+4 = 19
22. char VS nchar VS nvarchar 数据类型 字符编码 char( n=10 ) 非 Unicode nchar( n=10 ) Unicode nvarchar( n=10 ) Unicode 存储大小 固定10个字节 固定20个字节 非固定字节 “X” 58 5800 5800 数据类型 存储值“XX” 占用空间 char( n=10 ) 58582020202020202020 10 nchar( n=10 ) 5800580020002000200020002000200020002000 20 nvarchar( n=10 ) 58005800 4
23. 非聚集索引存储结构 MSDN
24. (堆表)非聚集索引存储结构 01 --Create Database 02 USE master 03 GO 04 IF EXISTS(SELECT name FROM sys.databases WHERE name = 'Heap_NonCluster') 05 DROP DATABASE Heap_NonCluster 06 GO 07 CREATE DATABASE Heap_NonCluster 08 GO 09 10 USE Heap_NonCluster 11 GO 12 13 --Heap 14 CREATE TABLE Page_NonCluster 15 ( 16 a INT IDENTITY, 17 b CHAR(5) DEFAULT 'xxxxx', 18 c VARCHAR(50) DEFAULT 'ccccc' 19 ) 20 GO 21 22 --Insert 23 INSERT INTO Page_NonCluster DEFAULT VALUES 24 GO 2000 25 26 --Index 27 CREATE NONCLUSTERED INDEX idx_ab ON Page_NonCluster(a,b)
25. (堆表)非聚集索引idx_ab(ind) 01 --idx_ab 02 DBCC IND (Heap_NonCluster, Page_NonCluster, 5) 03 GO
26. (堆表)非聚集索引idx_ab(page) 01 DBCC TRACEON (3604) 02 GO 03 DBCC PAGE(Heap_NonCluster, 1, 420, 3) 04 GO 05 DBCC PAGE(Heap_NonCluster, 1, 420, 1) 06 GO
27. (堆表)非聚集索引idx_ab(Root data) 01 DATA: 02 Slot 0, Offset 0x60, Length 27, DumpStyle BYTE 03 04 Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 27 05 Memory Dump @0x000000001004A060 06 0000000000000000: 16010000 00787878 78787700 00000100 0000a101 07 .....xxxxxw......... 08 0000000000000014: 00000100 030000 ....... 09 10 Slot 1, Offset 0x7b, Length 27, DumpStyle BYTE 11 Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 27 12 Memory Dump @0x000000001004A07B 13 0000000000000000: 16610100 00787878 78787900 00000100 4300a301 14 .a...xxxxxy.....C... 15 0000000000000014: 00000100 030000 .......
28. (堆表)非聚集索引idx_ab(Root) 原始数据 166101000078787878787900000001004300A30100000100030000 16 61010000 7878787878 79000000 0100 4300 A3010000 0100 0300 00 行头 索引 键值 INT 索引 键值 CHAR 页号 文件号 槽号 页号 文件 号 NULL 位图 16 353 xxxxx 121 1 67 键值所在记录的RID (1:121:67) 419 1 键值所在叶结点的 数据页(1:419) 3 0
29. (堆表)非聚集索引idx_ab(Leaf data) 01 DATA: 02 Slot 0, Offset 0x60, Length 21, DumpStyle BYTE 03 Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 21 04 Memory Dump @0x000000001129A060 05 0000000000000000: 16610100 00787878 78787900 00000100 43000300 06 .a...xxxxxy.....C... 07 0000000000000014:00 . 08 09 Slot 1, Offset 0x75, Length 21, DumpStyle BYTE 10 Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 21 11 Memory Dump @0x000000001129A075 12 0000000000000000: 16620100 00787878 78787900 00000100 44000300 13 .b...xxxxxy.....D... 14 0000000000000014: 00
30. (堆表)非聚集索引idx_ab(Leaf) 原始数据 166101000078787878787900000001004300030000 16 61010000 7878787878 79000000 0100 4300 0300 00 行头 索引 键值 INT 索引 键值 CHAR 页号 文件 号 槽号 索引字 NULL 段个数 位图 16 353 xxxxx 121 1 67 键值所在记录的RID (1:121:67) 3 0
31. (堆表)非聚集索引idx_ab(row data) 01 DATA: 02 Slot 0, Offset 0x60, Length 25, DumpStyle BYTE 03 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS 04 Record Size = 25 05 Memory Dump @0x00000000122BA060 06 0000000000000000: 30000d00 61010000 78787878 78030000 01001900 0.......xxxxx....... 07 0000000000000014: 63636363 63 ccccc 08 09 Slot 1, Offset 0x79, Length 25, DumpStyle BYTE 10 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS 11 Record Size = 25 12 Memory Dump @0x00000000122BA079 13 0000000000000000: 30000d00 62010000 78787878 78030000 01001900 0.......xxxxx....... 14 0000000000000014: 63636363 63 ccccc
32. (堆表)非聚集索引idx_ab(row) 原始数据 30000D00610100007878787878030000010019006363636363 字节 数 原始 数据 分割 2 2 4 5 2 1 2 2 5 3000 0D00 61010000 7878787878 0300 00 0100 1900 6363636363 行头 键值 INT 键值 CHAR 字段 NULL 变长字 变长字段 键值 个数 位图 段个数 偏移量 VAR CHA R 可读数据 3000 13 353 分解 xxxxx 3 0 1 25 2+2+4+5 = 13 2+2+4+5+2+1+2+2+5 = 25 ccccc
33. 根结点索引页 1 (堆表)非聚集索Root(1:42引0) idx_ab(结构) 1166 353 xxxxx 121 1 67 419 1 30 16 xxx…... (1:XXX:XX) (1:XXX) 3 0 xxx 96 分支结点索引页 2 Leaf(1:419) 16 xxx…... 16 xxx…... 16 xxx…... 1616 353 xxxxx (1:121:0) (1:121:1) (1:121:2) 121 1 2 0 2 0 2 0 67 3 0 2820 1912 1004 96 下一页 上一页 3 Leaf(1:XXX) 16 xxx 16 xxx 16 xxx 16 xxx (1:15431:3) (1:15431:4) (1:15434:0) (1:15434:1) 2 0 2 0 2 0 2 0 1004 96 堆表记录数据页 4 Page(1:121) 31000 1000 1000 1000 1000 13 1608 1608 1608 1608 353 xxxxx 3 98 xxx 99 xxx 100 xxx 101 xxx 01 xxx xxx xxx xxx 25 ccccc 3 0 3 0 3 0 3 0 6540 4929 3318 1707 96 PPaaggee PPaaggee
34. 聚集索引表的存储结构 MSDN
35. 聚集索引表的存储结构 01 --Create Database 02 USE master 03 GO 04 IF EXISTS(SELECT name FROM sys.databases WHERE name = 'ClusterIndex') 05 DROP DATABASE ClusterIndex 06 GO 07 CREATE DATABASE ClusterIndex 08 GO 09 10 USE ClusterIndex 11 GO 12 13 --Create Table 14 CREATE TABLE [dbo].[Page_Cluster]( 15 [a] [int] IDENTITY(1,1) NOT NULL, 16 [b] [char](5) DEFAULT 'xxxxx', 17 [c] [varchar](50) DEFAULT 'ccccc', 18 CONSTRAINT [PK_Page_Cluster] PRIMARY KEY CLUSTERED([id]) 19 ) 20 GO 21 22 --Insert 23 INSERT INTO Page_Cluster DEFAULT VALUES 24 GO 2000
36. 聚集索引(Root data) 01 DATA: 02 Slot 0, Offset 0x60, Length 11, DumpStyle BYTE 03 Record Type = INDEX_RECORD Record Attributes = Record Size = 11 04 Memory Dump @0x000000001197A060 05 0000000000000000: 0646bef3 fe5d0000 000100 .F...]..... 06 07 Slot 1, Offset 0x6b, Length 11, DumpStyle BYTE 08 Record Type = INDEX_RECORD Record Attributes = Record Size = 11 09 Memory Dump @0x000000001197A06B 10 0000000000000000: 062c0100 00770000 000100 .,...w.....
37. 聚集索引(Root) 原始数据 062C010000770000000100 字节 数 1 原始 数据 分割 06 4 2C010000 4 77000000 2 0100 行头 聚集 键值 页号 文件 号 可读 数据 06 300 分解 119 1
38. 聚集索引(Leaf data) 01 DATA: 02 Slot 0, Offset 0x60, Length 25, DumpStyle BYTE 03 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS 04 Record Size = 25 05 Memory Dump @0x000000001197A060 06 0000000000000000: 30000d00 2c010000 78787878 78030000 01001900 0...,...xxxxx....... 07 0000000000000014: 63636363 63 ccccc 08 09 Slot 1, Offset 0x79, Length 25, DumpStyle BYTE 10 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS 11 Record Size = 25 12 Memory Dump @0x000000001197A079 13 0000000000000000: 30000d00 2d010000 78787878 78030000 01001900 0...-...xxxxx....... 14 0000000000000014: 63636363 63 ccccc
39. 聚集索引(Leaf) 原始数据 30000D002C0100007878787878030000010019006363636363 字节 数 原始 数据 分割 2 2 4 5 2 1 2 2 5 3000 0D00 2C010000 7878787878 0300 00 0100 1900 6363636363 行头 定长 字段 偏移 量 键值 INT 键值 CHAR 字段 NULL 变长字 个数 位图 段个数 键值 VAR CHA R 可读数据 3000 13 分解 119 xxxxx 3 0 1 25 2+2+4+5 = 13 2+2+4+5+2+1+2+2+5 = 25 ccccc
40. 堆表 VS 聚集索引表 堆表 聚集索引表  index_id的值为0  数据存储没有特定的顺序  扫描表或非聚集索引检索数据  数据页之间没有指针关联  非聚集索引保存RowID  不用额外的空间去存储聚集索引  不用额外维护聚集索引  index_id的值为1  数据存储基于聚集索引键顺序存储  聚集索引键或非聚集索引检索数据  数据页之间有指针链接(双向链表)  非聚集索引保存聚集键值(例如ID)  需要额外的空间存储聚集索引  INSERT、UPDATE、DELETE操作时 需要额外维护聚集索引
41. 聚集索引 VS 非聚集索引 聚集索引 非聚集索引  一个表只能有一个聚集索引  聚集索引确定数据的物理顺序  叶结点就是数据结点  index_id的值为1  找到索引就找到数据了  建议聚集索引值是唯一的  一个表能有999个非聚集索引(2008)  跟物理顺序没有直接关系  叶结点指向数据页  index_id的值为>1  通过RID或者聚集键值找数据  建议数据选择性高就可以
42. 行溢出提纲  什么是行溢出?  什么时候会发生行溢出?  行溢出的存储结构到底长什么样子?  行溢出带来什么问题?  怎么规避这些问题?
43.  什么是行溢出?  In-row data  Row-overflow data  LOB data 行溢出  什么时候会发生行溢出?  行溢出数据只会发生在变长字段上,变长列的长度不能超过标准变长列 最大值8000个字节的限制  包括行头系统信息和所有定长列和变长系统信息的所有长度不能超过 8060字节  变长列的实际长度一定要超过24个字节  变长列不能是聚集索引键的一部分
44. 行溢出 01 USE master 02 GO 03 IF EXISTS(SELECT name FROM sys.databases WHERE name = 'Overflow') 04 DROP DATABASE [Overflow] 05 GO 06 CREATE DATABASE [Overflow] 07 GO 08 09 USE [Overflow] 10 GO 11 CREATE TABLE [HeapPage_Overflow] ( 12 Id INT, 13 VarCol1 VARCHAR (6000), 14 VarCol2 VARCHAR (3000)) 15 GO 16 17 INSERT INTO [HeapPage_Overflow] 18 VALUES (1,REPLICATE('a',6000),REPLICATE('b',3000)) 19 GO
45. 行溢出(data) 01 DATA: 02 Slot 0, Offset 0x60, Length 3041, DumpStyle BYTE 03 Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS 04 Record Size = 3041 05 Memory Dump @0x000000001006A060 06 07 0000000000000000: 30000800 01000000 03005802 002980e1 ?0.........X..).. 08 0000000000000010: 0b020000 00010000 00804a00 00701700 ?..........J..p.. 09 0000000000000020: 00590000 00010000 00626262 62626262 ?.Y.......bbbbbbb
46. 行溢出(行内数据) 原始数据 300008000100000003005802002980E10B0200000001000000804A0000701700005900000001000000626262…… 字节数 原始数据 分割 2 2 4 2 1 2 2 2 24 3000 3000 0800 01000000 0300 00 0200 2980 E10B 0200000001000000804A0000701700005900000001000000 626262 行头 定长字段 偏移量 键值 INT 字段 NULL 变长字 变长字段 变长字段 个数 位图 段个数 偏移量 偏移量 行溢 出指针 键值 VARCHAR 可读数据 3000 8 1 3 0 2 41 3041 0200000001000000804A0000701700005900000001000000 bbb 分解 24
47. 行溢出(指针) 原始数据 0200000001000000804A0000701700005900000001000000 字节数 原始数据 分割 1 2 1 4 02 0000 00 01000000 4 804A0000 4 70170000 4 59000000 2 2 0100 0000 类型 标识 B树 级别 未使 用 序列 号 Timestamp 溢出字段长度 页号 文件号 槽号 可读数据 2 0 0 分解 1 1249902592 6000 89 1 0 键值所在记录的RID (1:89:0)
48. 行溢出(溢出数据) 原始数据 08007E170000804A000000000300616161…… 字节数 2 2 8 2 6000 原始数据 分割 0800 7E17 0000804A00000000 0300 616161…… 变长 字段 行头 偏移量 Blob Id 数据 类型 键值 VAR CHA R 可读数据 分解 8 6014 1249902592 3 2+2+8+2 = 14 14+6000 = 6014 aaa……
49. 行溢出(逻辑图) Page(1:93) 96字节 30000800010000 0003005802002980E10B 02000000010000 00804A00007017000059 00 000001000000 626262…… 96 页头 行记录 空闲空间 行偏移量 Page(1:89) 96字节 08007E170000804A000000000300 616161…… 96 页头 行记录 空闲空间 行偏移量 行溢出指针
50. 行溢出  行溢出带来什么问题?  增加随机IO  增大内存中Page  增大行记录占用的空间  怎么规避这些问题?(行溢出占用比例较大)  使用大对象存储数据,所有列值在其它页集中存储  分开存储,垂直切分表
51. LOB  text、ntext和image  默认情况下,LOB数据不存储在行内记录  6个字节的指针指向LOB数据块  ROOT结构,由ROOT结构包含的指针指向真实的LOB数据块  text in row  varchar(MAX)、nvarchar(MAX)和varbinary(MAX)  字段值 < 8000个字节  字段值 > 8000个字节  large value types out of row
52. LOB  <64B  64B~40KB  40KB~16MB  >16MB  text in row=500
53. 行内数据 Page 120 (IN_ROW Data) 行头数据 16字节LOB数据的 ROOT指针 Page 126 (LOB Data) 64B~40KB14字节的行头数据 8040字节 LOB数据块 LOB数据块 Page 127 (LOB Data) Page 118(LOB Data) ROOT指针 24字节的 12字节LOB 12字节LOB 行头数据 数据块指针 数据块指针 12字节LOB 12字节LOB 12字节LOB 数据块指针 数据块指针 数据块指针 14字节的行头数据 8040字节 LOB数据块 LOB数据块 Page 145 (LOB Data) 14字节的行头数据 8040字节 LOB数据块 Page 142 (LOB Data) 14字节的行头数据 8040字节 LOB数据块 Page 144 (LOB Data) 14字节的行头数据 8040字节 LOB数据块 LOB数据块 LOB数据块 LOB数据块
54. 更多信息请参考《SQL Server性能调优实战》