聚集表(clustered table)data page中的数据行可以无序

误区

一直以为只要一个表含有聚集索引,那么在data page中的数据行是排序的。比如原来data page中有1.2.4.5.6这样四条记录,那么我要插入3这条记录,应该是先将456三条记录往后移,然后再插入3这条记录。这样的理解是错误的。

正解

首先从上面的数据行插入说起,如果要移动后面的记录来插入中间的记录,这么会影响性能,这是不允许的。sql server肯定有其他办法来实现有序。今天读inside sql server 2005第七章的时候才明白,其实data page内的数据可以无序,但是可以通过data page中的OFFSET TABLE实现有序。

实验

我们需要创建一个测试表,并在一个字段上添加聚集索引,代码如下

use TESTDB;
--step1:创建测试表,有id和name 两列
create table t1
(    
    id int not null,
    name char(10) not null
);

--step2:在id字段上创建聚集索引
create clustered index idx_t1_id on t1(id);

--然后插入一条记录
insert into t1 values(1,'zhangsan');

--通过dbcc page找出其数据存储方式
dbcc ind(TESTDB,T1,1);
dbcc traceon(3604);
dbcc page(TESTDB,1,118,1);

上面实验得到的结果如下所示:

DATA:
Slot 0, Offset 0x60, Length 21, DumpStyle BYTE
Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP     Record Size = 21
Memory Dump @0x000000000F82A060
0000000000000000:   10001200 01000000 7a68616e 6773616e †........zhangsan 
0000000000000010:   20200300 00††††††††††††††††††††††††††  ...   
OFFSET
TABLE: Row - Offset 0 (0x0) - 96 (0x60)

总结:

  1. 在data中,010000007a68616e 6773616e表示的就是(1,'zhangsan')。
  2. 在offset table中,我们看到第一个offset=96,这是因为header的长度就是96,offset的意思就是第一条记录距离data page起点的偏移量。因为前面96字节存放了header信息,所以第一个偏移量就是96了。

接下来我们需要再插入另外一条记录(3,'lisi'),之所以是3是因为我们后面需要插入2这条记录,这也是为了说明data page中数据行可以无序。执行下面代码插入数据并插卡data page内容:

--再插入另外一条记录
insert into t1 values(3,'lisi');
--查看页面信息
dbcc page(TESTDB,1,118,1);

上述查询过结果如下:

DATA:
Slot 0, Offset 0x60, Length 21, DumpStyle BYTE
Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP     Record Size = 21
Memory Dump @0x000000000D89A060
0000000000000000:   10001200 01000000 7a68616e 6773616e †........zhangsan 
0000000000000010:   20200300 00††††††††††††††††††††††††††  ...            

Slot 1, Offset 0x75, Length 21, DumpStyle BYTE
Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP     Record Size = 21
Memory Dump @0x000000000D89A075
0000000000000000:   10001200 03000000 6c697369 20202020 †........lisi     
0000000000000010:   20200300 00††††††††††††††††††††††††††  ...            

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

总结:

  1. 在offset table中多了一个row,并且两个offset相减刚好等于一个row的length=21.
  2. 在slot1中就是我们新插入的数据03000000 6c697369 20202020,这个可以通过求16进制得到,这里不再赘述。

接下来我们要插入一条记录(2,'wangwu'),这条记录中的id=2,刚好排在上面两条记录中间,我们查看插入这条结果以后会是什么结果。

insert into t1 values(2,'wangwu');
dbcc page(TESTDB,1,118,1);

执行上述查询以后我们得到如下结果:

DATA:
Slot 0, Offset 0x60, Length 21, DumpStyle BYTE
Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP     Record Size = 21
Memory Dump @0x000000000F82A060
0000000000000000:   10001200 01000000 7a68616e 6773616e †........zhangsan 
0000000000000010:   20200300 00††††††††††††††††††††††††††  ...            

Slot 1, Offset 0x8a, Length 21, DumpStyle BYTE
Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP     Record Size = 21
Memory Dump @0x000000000F82A08A
0000000000000000:   10001200 02000000 77616e67 77752020 †........wangwu   
0000000000000010:   20200300 00††††††††††††††††††††††††††  ...            

Slot 2, Offset 0x75, Length 21, DumpStyle BYTE
Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP     Record Size = 21
Memory Dump @0x000000000F82A075
0000000000000000:   10001200 03000000 6c697369 20202020 †........lisi     
0000000000000010:   20200300 00††††††††††††††††††††††††††  ...            

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

总结:

  1. 新插入的数据应该是0200000077616e67 77752020,这条记录是在slot1中,而原先slot1中的数据跑到了现在slot2的地方了,所以slot是排序的,但是slot并不是数据行的实际物理位置。
  2. 真正的实际物理位置应该通过offset来得到。我们发现新查询的数据的offset=0x8a,比前面两条记录的offset要大。所以证明在data page中,数据行可以使无序的。
  3. 我们再来查看offset table中的内容,我们发现row是有序排列的,但是offset是无序的。data page 正是通过offset来实现数据行的有序。
原文地址:https://www.cnblogs.com/xwdreamer/p/2614465.html