star870126 发表于 2018-10-16 09:55:18

SQL Server2008存储结构之堆表、行溢出

  上在页的尾部还有个行偏移矩阵,记录了每条记录的起始位置,每条记录需要2个字节来记录该位置,所以62条记录共计124个维护字节,加上293个剩余空间和实际已使用的7775个字节,刚好8192个字节,即一页。

  从Offset table和page结构可以知道,第一条记录从第96个字节开始。
IDnameTypeotherdescrible1name11111111111111111111111111111111111.8NULL  如前文所说,关于数据的存储从第96个字节开始

  关于数据行的结构我们还可以采用稍微宏观一些的视角来查看。

  其中状态A为如下说明:
  bit0:版本信息,在SQL Server 2005/08总是为0
  bit1-3: 0=(primary record);1=(forwarded record);2=(forwarding stud);3=(index record);4=(溢出数据);5=(ghost索引记录);6=(ghost数据记录)
  bit4:表示存在NULL位图(在数据行里SQL2005/08总存在NULL位图)
  bit5:表示存在变长列
  bit6:未启用
  bit7:表示存在幽灵记录
  本例中30->00110000 它是一个行属性的位图 从高位存到地位(右边第一位是bit0),bit4为1即存在变长列的字段,因为在SQLServer2005/2008中总存在NULL位图,所以bit5也为1。
  状态位B在SQLServer2005//2008中未启用,所以为00
  记录定长部分的长度为2个字节,是所有定长字段的长度之和加4,该处为int类型4个字节,char(100)为100个字节,再加上4,所以为108,换算成16进制即6c。
  紧跟其后的为定长字段的内容,即ID字段的4个字节和TYPE字段的100个字节。
  固定长度的字段数据之后,是该表的总字段数,用两个字节表示,本表包括5个字段所以为05 00。
  NULL位图:f0->11110000 因为该表只有列 所以只需要看后面个,1表示该行的对应列为NULL或者该位图未使用。本表前4个字段不为空,第5个为空,第6-8未使用。
  接下来是行内存储数据的变长列的数目:0200->00000000 00000010=2 表示该行存储了列name和other字段的数据。
  第一变长列数据终止位置为:7a00->00000000 01111010=122=1+1+2+(4+100)+2+ceiling(5/8)+2+2+2+len(“name1”)
  第二变长列数据终止位置:7b00->00000000 01111011=123 实际上就是在前者的基础上加了第二个变长列的字段长度。
  1+1+2+(4+100)+2+ceiling(5/8)+2+2+2+len(“name1”)+len(“8”)
  第一列变长列的数据: 6e616d 6531换算成字符即'name1'
  第二列变长列的数据:38换算成字符即8  
  下面让我们将该记录的describle字段更新为非空值后,再看看该记录存储结构相应的变化。
UPDATE testheap SET describle='abc' WHERE>  再次使用dbcc page(testdb,1,224,1)命令

  我们不难发现状态A,状态B,定长长度、定长内容和字段总数是没有发生任何变化的。
  NULL位图部分变成了e0即11100000,表示describle字段即第五个字段不为空了
  第一个和第二个变长列数据终止位置分别加了2个长度,这是因为当第三个变长列变更为非空后,自动添加了2个字节的第三个字段的维护字段
  第一个变长列数据终止位置从7a00变更为7c00
  第二个变长列数据终止位置从7b00变更为7d00
  新增加的第三个变长列终止位置为8000
  同时在第一、二列变长列的数据后面新增加了616263,即字符串”abc”
  还有一个最显著的区别就是该记录的偏移位置显然转到了尾部,即5F1E的位置;但很奇怪的是该记录原来的位置上还保留着原值,并没有删除掉。也就是说对于该记录而言,应该是先删除,然后又添加了一条新纪录,只是把指针指向了新的偏移地址而已。
  最后观察一下记录是如何删除的
DELETE FROM testheap WHERE>  当我们对比一下删除前后两条记录的信息,发现基本上原来的位置上数据没有发生任何变化,只是原来的slot1和slot2已经不存在了。即SQL Server认为该数据已经不存在了。

  行溢出页面
USE TESTDB  CREATE TABLE testOverFlow
  (
  ID
INT>NAME1 VARCHAR(5000),  NAME2 VARCHAR(
5000)  )
  INSERT INTO testOverFlow (NAME1,NAME2)
SELECT REPLICATE('A',5000),REPLICATE('AB',2500)UNIONSELECT REPLICATE('B',5000),REPLICATE('BA',2500)SELECT * FROM testOverFlowSELECT type_desc  total_pages,used_pages,data_pages,
  testdb.dbo.f_get_page(first_page) first_page_address,
  testdb.dbo.f_get_page(root_page) root_address,
  testdb.dbo.f_get_page(first_iam_page) IAM_address
  FROM sys.system_internals_allocation_units
  WHERE container_id IN (
SELECT partition_id FROM sys.partitions  WHERE object_id in (
SELECT object_idFROM sys.objects  WHERE name IN (
'testoverflow')))  DBCC TRACEON(
3604)  DBCC PAGE(testdb,
1,54242,2)--行内数据  DBCC PAGE(testdb,
1,52343,2)--行迁移数据--同时我们也可以通过dbcc ind获取所有数据页面地址,然后进行页面信息显示  TRUNCATE TABLE tablepage;
  INSERT INTO tablepage EXEC (
'DBCC IND(testdb,testOverFlow,1)');SELECT  PagePID,IAMPID,ObjectID,IndexID,Pagetype,IndexLevel,
  NextPagePID,PrevPagePID
  FROM tablepage
  在NAME2字段之前和普通的行记录信息是一致的,我们只从NAME2字段开始就可以了。NAME2字段在NAME1字段之后,保存了以下内容,即改列的溢出列类型、节点类型、数据库更新次数、字段长度、指向OVERFLOW页的指针。
02000000010000009d7500008813 000077cc0000 0100 0000000溢出列类型节点类型Lob数据更新次数ID未知字段长度行溢出指针RowOVerFlow011973223424 50001:52343:0  让我们再来看一下第52343页看一下行溢出页的数据情况,该页面首先是一个LOB类型的页面,然后主要包括该字段的长度、关联ID,和数据行;很显然行 内数据和溢出行数据的关联是通过一个行溢出指针和ID进行的;因此对某个数据查询而言,首先要找到该记录的信息,同时如果发生行溢出,还有根据该列的行溢 出指针和关联ID,才能找到整条记录。
1个字节1个字节2个字节8个字节4个字节2个字节0800961300009d75000000000300状态A状态B字段长度IDunkown类型即包含行溢出 5014(同变长字段)1973223424未知lob数据行
页: [1]
查看完整版本: SQL Server2008存储结构之堆表、行溢出