《Microsoft Sql server 2008 Internals》读书笔记--第六章Indexes:Internals and Management(9)

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

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

前面我们主要了解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的所有记录的物理位置必须被改变,从而使非聚集索引的前一个行定位器失效。

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


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

查看结果:

PageFID PagePID
1 248

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

现在我们来更新一行

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

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

我们再来看DBCC的输出:


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

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)的一些基本机制。下文将介绍表级和索引级数据修改时索引的维护和整理。

邀月注:本文版权由邀月 和CSDN共同所有,转载请注明出处。
助人等于自助! [email protected]

你可能感兴趣的:(sql,server,2008)