zoukankan      html  css  js  c++  java
  • sqlserver 数据存储

    /*

        测试数据存储

    */

    准备工作

    -- =============================================

    -- 创建测试数据库

    -- =============================================

    create database test

    go

    -- =============================================

    -- 创建测试表

    -- =============================================

    use test

    go

     

    create table test(

    id int identity(1,1) not null,

    name varchar(10) not null

    )

    go

    -- =============================================

    -- 导入数据

    -- =============================================

    declare @i int

    set @i=0

    while @i<100

    begin

        insert into test(name) values('aaa')

        set @i=@i+1

    end

    go

    declare @i int

    set @i=0

    while @i<100

    begin

        insert into test(name) values('bbb')

        set @i=@i+1

    end

    go

    declare @i int

    set @i=0

    while @i<100

    begin

        insert into test(name) values('ccc')

        set @i=@i+1

    end

    go

    declare @i int

    set @i=0

    while @i<100

    begin

        insert into test(name) values('ddd')

        set @i=@i+1

    end

    go-- =============================================

    -- 查看相关信息

    -- =============================================

    SELECT COUNT(*) FROM test

    select * from sys.partitions where [object_id]=object_id('test')

    select * from sys.objects where name='test' and type_desc='USER_TABLE'

    select * from sys.indexes where [object_id]=2105058535

    EXEC sp_spaceused 'test'

    GO

    结果如下:

                     

    测试步骤

    1、我们需要找到数据存放于哪些数据页上面。

    打开一个session 1 执行以下代码:

    begin tran

    update test set name='fff' where id =1

     

    waitfor delay '00:00:05'

     

    select * from test where id=2

    同时打开session 2 执行以下代码

    begin tran

     

    update test set name='fff' where id =2

     

     

    select * from test where id=1

    同时打开session 3 执行以下代码:

    select * from sys.dm_os_waiting_tasks where session_id>50

    结果如下:

    然后执行以下代码:

    dbcc traceon(3604)

    go

    dbcc page('test',1,21,3)

    go

    结果如下:

    从截图的红框部分可以发现,page(1:21)上存储了385条记录(具体说明见之后的页头信息说明),但是我们之前保存了400条记录,那剩下的15条记录存放在哪一个数据页?

    回滚session 1 、 session 2的事务。

    在session 1 中执行以下代码:

    begin tran

     

    update test set name='fff' where id =386

     

    waitfor delay '00:00:05'

     

    select * from test where id=387

    同时在session 2中执行以下代码:

    begin tran

    update test set name='fff' where id =387

     

     

    select * from test where id=386

    同时打开session 3 执行以下代码:

    select * from sys.dm_os_waiting_tasks where session_id>50

    结果如下:

    然后执行以下代码:

    dbcc traceon(3604)

    go

    dbcc page('test',1,55,3)

    go

    结果如下:

    因为没有索引,所以页面无法知道下一页是那个页面。

    m_prevPage = (0:0) :前一页的页码           m_nextPage = (0:0) :后一页页码

    2、创建聚集索引

    -- =============================================

    -- 创建聚集索引

    -- =============================================

    create clustered index ix_test on test(id)

    drop index ix_test on test

    获取创建索引之后的数据页,同样需要把之前的方法再操作一遍,此时,我获取到的第一个数据页为page(1:79)

    -- =============================================

    -- 查看数据页存储

    -- =============================================

    dbcc traceon(3604)

    go

    dbcc page('test',1,79,3)

    go

    结果:

    DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。

    PAGE: (1:79)

     

     

    BUFFER:

     

     

    BUF @0x04C83CE0

     

    bpage = 0x1DD2A000                   bhash = 0x00000000                   bpageno = (1:79)

    bdbid = 7                            breferences = 0                      bcputicks = 0

    bsampleCount = 0                     bUse1 = 27502                        bstat = 0xc0010b

    blog = 0x1212121b                    bnext = 0x00000000                    

    bdbid=7 :数据库ID,可以使用DB_NAME(7)得到数据库名为测试库test。

    Bpageno=(1:79)  :数据页编号,1指文件ID,79指数据页ID

    PAGE HEADER: (页头信息):

     

     

    Page @0x1DD2A000

     

    m_pageId = (1:79)                    m_headerVersion = 1                  m_type = 1

    m_typeFlagBits = 0x0                 m_level = 0                          m_flagBits = 0x0

    m_objId (AllocUnitId.idObj) = 30     m_indexId (AllocUnitId.idInd) = 256 

    Metadata: AllocUnitId = 72057594039894016                                 

    Metadata: PartitionId = 72057594038910976                                 Metadata: IndexId = 0

    Metadata: ObjectId = 2105058535      m_prevPage = (0:0)                   m_nextPage = (1:89)

    pminlen = 8                          m_slotCnt = 368                      m_freeCnt = 0

    m_freeData = 7456                    m_reservedCnt = 0                    m_lsn = (30:248:60)

    m_xactReserved = 0                   m_xdesId = (0:0)                     m_ghostRecCnt = 0

    m_tornBits = 0              

    m_pageId = (1:79) :数据页编号

    m_prevPage = (0:0) :上一页编号

    m_nextPage = (1:89) :下一页编号,可以执行dbcc page(‘test’,1,89,3)来查看m_prevPage值必为(1:79)

    Metadata: PartitionId = 72057594038910976 :所属分区ID

    Metadata: AllocUnitId = 72057594039894016 :包含该页的分区单元ID

    Metadata: ObjectId = 2105058535 :该页所属的对象ID

    m_level = 0 :该页面在索引中的级别(数据页为0)

    m_slotCnt = 368 :该页存储的数据行数,可以执行dbcc page(‘test’,1,89,3)来查看m_slotCnt必为32,368+32=400,与该表插入的行数完全一致。

    Allocation Status

     

    GAM (1:2) = ALLOCATED                SGAM (1:3) = ALLOCATED               

    PFS (1:1) = 0x64 MIXED_EXT ALLOCATED 100_PCT_FULL                         DIFF (1:6) = CHANGED

    ML (1:7) = NOT MIN_LOGGED           

     

    Slot 0 Offset 0x60 Length 20

     

    Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

    Record Size = 20                    

    Memory Dump @0x0B2FC060

     

    00000000:   30000800 01000000 03000002 00110014 †0...............        

    00000010:   00616161 ††††††††††††††††††††††††††††.aaa                    

     

    Slot 0 Column 67108865 Offset 0x0 Length 0 Length (physical) 0

     

    DROPPED = NULL                       

    Slot 0 Column 1 Offset 0x4 Length 4 Length (physical) 4 :第1行数据

    id = 1  :字段id的值,实际长度为 Length 4 Length (physical) 4                            

    Slot 0 Column 2 Offset 0x11 Length 3 Length (physical) 3

    name = aaa   :字段name的值,实际长度为Length 3 Length (physical) 3     

    注意:

    执行语句:update test set name='fffff' where id=1

    再重新执行语句dbcc page(‘test’,1,79,3) 可以发现,slot 0 这行没有值。Page(1:79)有值的首行是从slot 1开始。这个数据仅仅只是更新而不是删除,原有slot 0的值去哪里里了?

    执行dbcc page(‘test’,1,89,3) ,你会发现slot 32行变成了page(1:79)行的值。

    从这里可以看出:update操作的原理,删除原有得数据行,并将数据重新插入到这个对象所属页面的最后一行。               

    Slot 1 Offset 0x74 Length 20

     

    Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

    Record Size = 20                     

    Memory Dump @0x0B2FC074

     

    00000000:   30000800 02000000 03000002 00110014 †0...............        

    00000010:   00616161 ††††††††††††††††††††††††††††.aaa                    

     

    Slot 1 Column 67108865 Offset 0x0 Length 0 Length (physical) 0

     

    DROPPED = NULL                      

     

    Slot 1 Column 1 Offset 0x4 Length 4 Length (physical) 4

     

    id = 2                              

     

    Slot 1 Column 2 Offset 0x11 Length 3 Length (physical) 3

     

    name = aaa                          

     

    Slot 2 Offset 0x88 Length 20

     

    Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

    Record Size = 20                    

    Memory Dump @0x0B2FC088

     

    00000000:   30000800 03000000 03000002 00110014 †0...............        

    00000010:   00616161 ††††††††††††††††††††††††††††.aaa                    

     

    Slot 2 Column 67108865 Offset 0x0 Length 0 Length (physical) 0

     

    DROPPED = NULL                      

     

    Slot 2 Column 1 Offset 0x4 Length 4 Length (physical) 4

     

    id = 3                               

     

    Slot 2 Column 2 Offset 0x11 Length 3 Length (physical) 3

     

    name = aaa                         

    .

    .

    .

    .

    .

    .

    Slot 366 Offset 0x1cf8 Length 20

     

    Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

    Record Size = 20                    

    Memory Dump @0x0B2FDCF8

     

    00000000:   30000800 6f010000 03000002 00110014 †0...o...........        

    00000010:   00646464 ††††††††††††††††††††††††††††.ddd                    

     

    Slot 366 Column 67108865 Offset 0x0 Length 0 Length (physical) 0

     

    DROPPED = NULL                      

     

    Slot 366 Column 1 Offset 0x4 Length 4 Length (physical) 4

     

    id = 367                            

     

    Slot 366 Column 2 Offset 0x11 Length 3 Length (physical) 3

     

    name = ddd                          

     

    Slot 367 Offset 0x1d0c Length 20

     

    Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

    Record Size = 20                    

    Memory Dump @0x0B2FDD0C

     

    00000000:   30000800 70010000 03000002 00110014 †0...p...........        

    00000010:   00646464 ††††††††††††††††††††††††††††.ddd                    

     

    Slot 367 Column 67108865 Offset 0x0 Length 0 Length (physical) 0

     

    DROPPED = NULL                      

     

    Slot 367 Column 1 Offset 0x4 Length 4 Length (physical) 4

     

    id = 368                            

     

    Slot 367 Column 2 Offset 0x11 Length 3 Length (physical) 3

     

    name = ddd                          

     

     

    DBCC 执行完毕。如果DBCC 输出了错误信息,请与系统管理员联系。

    结论

    1、在没有聚集索引(非聚集索引我也测试了,但dbcc page的结果与没有索引时保持一致)的情况下,数据页的m_pageId 、m_prevPage属性值都为(0:0),也就是说,无法指向下一页。

    但是,使用dbcc ind('test',2105058535,-1)可以看到页面的前后顺序(这里特别感谢肖磊),注意,由于我重新删除数据库并按照文章上面的步骤执行后,pageid有变化,已经对不上文章中的pageid。

    2、当出现了聚集索引,数据会迁移到别的存储页面中。这就说明:重建聚集索引时,数据可能会从一个页面移动到另外的页面,从而产生大量的IO。

    3、数据更新时,数据库先删除原有数据,同时再往改对象所存储的最后一页插入更新后的数据。这是武断的结论,感谢高文佳和肖磊,

    高文佳的意见是:

    对于update操作 在数据库上到底做UPDATE 还是做DELETE+INSERT,取决当前的空间是否能存放更新后的数据

    肖磊的意见是:

    m_freeCnt这个是指页面剩余空间,如果它是0,你再将其中一条记录update成更长的value,当然会引起页拆分。对于varchar类型,还有overflow的问题

    我通过测试,在更新第二个数据页上面的id为380的数据发现,完全符合肖磊的意见,当数据页剩余存储空间足够时,即m_freeCnt值不为0时,数据的位置不会发生改变。

     

     

  • 相关阅读:
    c语言数组的赋值问题
    英语------------单词被动形式的读音规律
    英语------------单词复数形式的规律
    一天半时间大致的学习了HTML和CSS.
    没有权威,只有努力
    java 对象赋值问题
    Java-Math
    StringBuffer
    java--String方法
    某个日期的下一天
  • 原文地址:https://www.cnblogs.com/FreeFire/p/4544389.html
Copyright © 2011-2022 走看看