《Pro SQL Server Internals, 2nd edition》的CHAPTER 1 Data Storage Internals中的Data Pages and Data Rows(翻
Posted ftcm-cxy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《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的结果
以上是关于《Pro SQL Server Internals, 2nd edition》的CHAPTER 1 Data Storage Internals中的Data Pages and Data Rows(翻的主要内容,如果未能解决你的问题,请参考以下文章
《Pro SQL Server Internals》部分翻译
《Pro SQL Server Internals》部分翻译