zoukankan      html  css  js  c++  java
  • 《Microsoft Sql server 2008 Internals》读书笔记第六章Indexes:Internals and Management(9)

     

    《Microsoft Sql server 2008 Internals》读书笔记订阅地址:

    http://www.cnblogs.com/downmoon/category/230397.html/rss

    《Microsoft Sql server 2008 Internals》索引目录:

    《Microsoft Sql server 2008 Internal》读书笔记--目录索引

    前面我们主要了解Inserting/Deleting Rows 时SQL Server的内部存储机制,这一篇我们来看看Update Rows的内部机制。

     ■Update Rows

    SQL Server更新行有多种方式,自动并隐性地为特定的操作选择最快的更新策略,在决定策略时,SQL Server评估将要影响行的数量,行是如何(通过查询或索引恢复、或者索引本身)被访问的,索引键是否发生变化等。更新在两种情况下发生:一是在原始行 改变一列值到新值。二是delete紧跟着一个insert。Update能被查询处理器或存储引擎管理。

     ■Moving Rows

    如果表的一行必须被移动到一个新的位置会发生什么?在SQL Server 2008中,在一个可变长度列被更新到一个新的更大的(原页面不再适应的)长度时,或在一个聚集索引或非聚集索引列(因为行被索引键逻辑排序而)发生改变 时,这种情况发生。例如,在lastname列上有一个聚集索引,一个lastname值为abbot的行被存储在表的开始部分,如果这个值被更新为 Zappa,这个行不得不移动到表的末尾部分。

    在前一章中,我们了解索引的结构,并知道非聚集索引的叶级包含一个行定位器,即书签,对应于 表中的每一个单独的行。如果表有一个聚集索引,行定位器就是这个表的索引键。如果聚集索引键被更新时,每一个非聚集索引(除了筛选非聚集索引可能例外)都 得跟着修改。当你决定在哪一列建立聚集索引时,记得这一点。在一个不易更改的列建产聚集索引是一个很great的想法,比如标识列。

     如 果一行因为它不适应原页面而被移动时,它仍然有相同的行定位器(换句话说,聚集索引键没有改变),没有非聚集索引需要被修改,即使表被移动到一个新物理位 置(文件组或分区架构)。非聚集索引被更新除非聚集索引键发生变化,而移动一个表的行并不改变它的索引键。

     在索引内幕的讨论中,你也注意 到如果一个表是Heap,存储在非聚集索引的行定位器实际上是行的物理位置。在SQL Server 2008中,如果一个Heap的一行移动到一个新页,该行保留一个指向原位置的转向指针。这个非聚集索引并不需要被改变,仍然指向原位置,从原位置,直接 指向新位置。在这种情况下,如果表移动到一个新位置,非聚集索引被更新,当Heap的所有记录的物理位置必须被改变,从而使非聚集索引的前一个行定位器失 效。

    这段非常拗口,我们看一个例子,

    USE Testdb;
    go
    DROP TABLE bigrows;
    go
    CREATE TABLE bigrows
    (
        a 
    int IDENTITY ,
        b 
    varchar(1600),
        c 
    varchar(1600)
    );
    go

    INSERT INTO bigrows
        
    VALUES (REPLICATE('a'1600), '');
    INSERT INTO bigrows
        
    VALUES (REPLICATE('b'1600), '');
    INSERT INTO bigrows
        
    VALUES (REPLICATE('c'1600), '');
    INSERT INTO bigrows
        
    VALUES (REPLICATE('d'1600), '');
    INSERT INTO bigrows
        
    VALUES (REPLICATE('e'1600), '');
    go

    建立一个表来存放页面数据

    代码
    IF OBJECTPROPERTY(object_id('sp_tablepages'), 'IsUserTable'IS NOT NULL
        
    DROP TABLE sp_tablepages;
    go

    CREATE TABLE sp_tablepages
    (
        PageFID         
    tinyint,
        PagePID         
    int,
        IAMFID          
    tinyint,
        IAMPID          
    int,
        ObjectID        
    int,
        IndexID         
    tinyint,
        PartitionNumber 
    tinyint,
        PartitionID     
    bigint,
        iam_chain_type  
    varchar(30),
        PageType        
    tinyint,
        IndexLevel      
    tinyint,
        NextPageFID     
    tinyint,
        NextPagePID     
    int,
        PrevPageFID     
    tinyint,
        PrevPagePID     
    int,
        
    CONSTRAINT sp_tablepages_PK
            
    PRIMARY KEY (PageFID, PagePID)
    );
    go

    查看结果:

    TRUNCATE TABLE sp_tablepages;
    INSERT INTO sp_tablepages
    EXEC ('DBCC IND (Testdb, bigrows, -1)' );

    SELECT PageFID, PagePID
    FROM sp_tablepages
    WHERE PageType = 1;
    go

     PageFID    PagePID
    1    248

     我们看到。这些行在一个PageID为248的页面中。

    现在我们来更新一行

    UPDATE bigrows
        
    SET c = REPLICATE('x'1600)
    WHERE a = 3;
    go

    再 来看结果:
    PageFID    PagePID
    1    248
    1    254

    很显然,增加了一个页面,因为原页面不够存放a=3的这行新值。

    我们再来看DBCC的输出:

    DBCC TRACEON(3604);
    go
    -- Be sure to enter YOUR PagePID:
    DBCC PAGE(testdb, 12481);
    go


    邀月工作室

    结果如图,你也可以使用以下命令直接查看结果:

     DBCC TRACEON(3604);
    go
    -- Be sure to enter YOUR PagePID:
    DBCC PAGE(testdb, 12541);
    go 

    Managing Forwarding Pointers

    转发指针允许你在一个Heap中修改数据而不用担心非聚集索引发生激烈的变化,如果一个已经转向的行需要再次被移动,原转向指针被更新以指向新的位置。另 外,如果转向行收缩到足够适应原页面,该记录可能移动到原来的位置,如果原页面还有空间。此时,转向指针将消除。

     在SQL Server的未来一个版本中,可能包含一些机制,如在一个Heap中执行物理识别,以除去转向指针。注意转向指针仅存在于Heap中。重新组织表的 Alter Table选项并不影响Heap。可以在一个Heap中整理一个非聚集索引,而不是更新表自身。目前,当一个转向指向被创建时,它永远在那儿不会移动, 除了几个例外:

    第一个例外是刚才提到的,行被收缩返回到原始位置。

    第二个例外,是整个数据库发生收缩,当一个文件被 收缩时书签实际上被重排了。收缩进程实际上并不生成转发指针。而那些对于收缩进程移动的页面,所包含的任何转向的行或存根都有有效地“反转向”了。

    第三个例外,如果转向行被删除或一个聚集索引被创建而不再是一个Heap。

    如果要获取一个表中的转向记录的数量,你可以使用 sys.dm_db_index_physical_stats函数。请看MSDN:
    http://msdn.microsoft.com/zh-cn/library/ms188917.aspx

    Update in place

     在 SQL Server 2008中,原地更新一个行时主要依据规则而不是意外。(updating a row in place is the rule rather than the exception),这意味着行准确在呆在相同的页面的相同位置,仅仅受影响的字节发生改变。此外,日志为每个原地更新操作包含了一个单独的记录,除非 表有一个更新触发器或被标记为用于复制。在这些情况下,更新将在原地发生,但日志不包含索引键列被更新时的Delete+Insert操作。

    在某些情况下当一个行不能被原地更新时,“非原地更新”的代价是最小的,因为非聚集索引的存储方式和转向的使用。事实上,也可以对仍保留在原始页的行进行非 原地更新。如果一个Heap被更新,并且页面有足够的空间,或一个有聚集索引的表被更新(而同时聚集键没有变化的情况下),更新在原地发生。如果聚集键发 生改变但是行不需要移动时,也可以在原地更新。比如,如果在包含连续键值(Able,Becker和Charlie)的lastname列有一个聚集索 引,你可能更新Becker为Baker,此时,行还在原来的位置,即使聚集索引键改变,SQL会执行一个原地更新。另外一方面,如果你更新“Able” 为“Buchner”,更新不能在原地发生。

    Update not in place

    如果正在更新聚集键时更新不能在原地发生,更新以“Delete+Insert”方式实行,在某些情况下,你可能得到一个混合更新:一些行被原地更新,一些不在。如果你更新索引键,SQL建立一个需要实行(Delete+inert)的行列表。列表在内存中,在不大的情况下,如果有必要可能会写入Tempdb 中,这个列表按键值和操作(Delete+Insert)排序。如果键值被改变的索引是不惟一的,Delete+Insert步骤被应用到表。如果是惟一 的,则执行单个的Update操作。

    本文主要介绍了更新行时数据页的变化及转向指针(Forwarding pointer)的一些基本机制。下文将介绍表级和索引级数据修改时索引的维护和整理。

    邀月注:本文版权由邀月和博客园共同所有,转载请注明出处。
    助人等于自助!  3w@live.cn
  • 相关阅读:
    GET POST方法长度限制(转)
    解决Android LogCat 输出乱码的问题(转)
    Supported Values for @SuppressWarnings(转)
    Gson通过借助TypeToken获取泛型参数的类型的方法(转)
    使用GSON和泛型解析约定格式的JSON串(转)
    HDU 4423 Simple Function(数学题,2012长春D题)
    VIM简单配置(windows)
    LightOJ 1074
    HDU 4763 Theme Section (2013长春网络赛1005,KMP)
    HDU 4764 Stone (2013长春网络赛,水博弈)
  • 原文地址:https://www.cnblogs.com/downmoon/p/1713201.html
Copyright © 2011-2022 走看看