行版本控制框架在 Microsoft SQL Server 中始终处于启用状态,并被多个功能使用。它除了提供基于行版本控制的隔离级别之外,还用于支持对触发器和多个活动结果集 (MARS) 会话的修改,以及 ONLINE 索引操作的数据读取。

        基于行版本控制的隔离级别是在数据库级别上启用的。访问已启用数据库的对象的任何应用程序可以使用以下隔离级别运行查询:已提交读隔离级别,通过将 READ_COMMITTED_SNAPSHOT 数据库选项设置为 ON 来使用行版本控制,如下面的代码示例所示:

       ALTER DATABASE AdventureWorks
       SET READ_COMMITTED_SNAPSHOT ON;


       为 READ_COMMITTED_SNAPSHOT 启用数据库后,在已提交读隔离级别下运行的所有查询将使用行版本控制,这意味着读取操作不会阻止更新操作。

覆盖列概览(Introduction to Covering Columns )

说明:本文是从Inside Microsoft SQL Server 2005 Query Tuning and Optimization一书的"Query Execution"一节的译文,有关详细内容可以阅读该书籍原文.
  对于堆或聚集索引的一张表,也叫"基表",包含(覆盖)了表的所有列,换句话说,非聚集索引只包含(覆盖)了表中的一小部分列,通过在非聚集索引来限制列的集合,SQL Server可以在每一页储存更多的行,这显然节约了空间,提高查找与扫描的效率,降低了I/O的操作数和页面数.然而,对于索引的扫描或查找来说,它只能返回该索引覆盖的那些列的记录行.
当创建覆盖列时,可以在非聚集索引上指定这些键列.如果基表是含有聚集索引的,位于该表上的每一个非聚集索引将覆盖聚集索引键,而不关心它们是否是非聚集索引键列的成员.在SQL Server 2005中,我们可以在非聚集索引上使用Create INDEX ...INCLUDE子语来添加额外的键列,注意:和索引键不同,INCLUDE中的列的次序并不重要.
下面我来通过一个示例说明,首先我们创建以下架构和对象:
Create TABLE T_heap (a int, b int, c int, d int, e int, f int)
Create INDEX T_heap_a ON T_heap (a)Create INDEX T_heap_bc ON T_heap (b, c)Create INDEX T_heap_d ON T_heap (d) INCLUDE (e)Create UNIQUE INDEX T_heap_f ON T_heap (f)Create TABLE T_clu (a int, b int, c int, d int, e int, f int)Create UNIQUE CLUSTERED INDEX T_clu_a ON T_clu (a)Create INDEX T_clu_b ON T_clu (b)Create INDEX T_clu_ac ON T_clu (a, c)Create INDEX T_clu_d ON T_clu (d) INCLUDE (e)Create UNIQUE INDEX T_clu_f ON T_clu (f)下面列举上面每一个索引的键列和覆盖列.
索引名称 键列 覆盖列
T_heap_a a a
T_heap_bc b,c b,c
T_heap_d d d,e
T_heap_f f f
T_clu_a a a,b,c,d,e,f
T_clu_b b,a a,b
T_clu_ac a,c a,c
T_clu_d d,a a,d,e
T_clu_f f a,f
注意:对于T_clu表中的每一个非聚集索引键列均包含一个聚集索引键(T_clu_f除外,它是一个唯一索引).T_clu_ac显式包含了索引的第一个键,而其他的索引并无显式包含a列.
对于创建的列在实际的索引查找和书签查询是有何不同呢?
下面我们来看一个例子:
     Select e from t_clu where b = 2初看,这个查询看起来符合索引查找的候选,然而,该索引并不覆盖列e,因而索引的扫描或查找并不能返回e列的值,其解决方法是很简单的,对于从非聚集索引中获取的每一行,我们可以通过聚集索引来查询e列的值,这种方法称为"书签查询",书签查询是一个指向堆或聚集索引行的一个指针.在非聚集索引中存储每一行的书签,这样每次在进行非聚集索引查询时,总是由非聚集索引转向基表中相对应的行.
书签查询
在上面的例子中,我们了解了SQL Server如何使用索引查找来有效地获取满足谓词上的数据,然而,我们也知道非聚集索引并不覆盖表的所有列.试想一下,若我们有这样一个在非聚集索引键上的谓词查询:select查询的列并未被索引覆盖,当SQL Server在非聚集索引上查找时,将会丢失一些需要的列,与之相反,如果在聚集索引或唯上执行扫描时,将获取所有列,由于要扫描表的每一行,其操作显然不是很有效.以下的查询就是如此:
Select [OrderId], [CustomerId] FROM [Orders] Where [OrderDate] = '1998-02-26'上面的查询与先前我们进行索引查找的用到的查询一样,唯一不同的是,这里我们选择了两列:OrderID和CustomerID.非聚集索引OrderDate列仅覆盖OrderID列.
SQL Server具有处理这一问题的方法,对于从非聚集索引中获取的每一行,它可能查询包含在聚集索引中的剩余列(这里是CustomerID),我们称该操作为"书签查询".书签查询是一个指向堆或聚集索引行的指针.SQL Server在非聚集索引中存储每一行的书签,这样一来,可以从非聚集索引直接转向基表中相对应的记录行.
SQL Server 2000使用指定的迭代器来实现书签查询,通过文本计划可以看出索引查找与书签查询迭代器:
|--Bookmark Lookup(BOOKMARK[Bmk1000]), OBJECT[Orders]))     |--Index Seek(OBJECT[Orders].[OrderDate]),        SEEK[Orders].[OrderDate]=Convert([@1])) orDERED FORWARD)在SQL Server 2005中将使用嵌套循环联接和聚集索引查找来实现,仅当基表含有聚集索引或RID查询(基表是堆),SQL Server 2005中的查询计划与SQL Server 2000中的计划有些不同,但逻辑上是相同的.聚集索引查找就是通过LOOKUP关键来实现的一种书签查询或通过属性Lookup=”1”.以下是SQL Server 2005的图形查询计划和文本查询计划:
|--Nested Loops(Inner Join, OUTER REFERENCES[Orders].[OrderID]))       |--Index Seek(OBJECT[Orders].[OrderDate]),          SEEK[Orders].[OrderDate]='1998-02-26') orDERED FORWARD)       |--Clustered Index Seek(OBJECT[Orders].[PK_Orders]),          SEEK[Orders].[OrderID]=[Orders].[OrderID]) LOOKUP orDERED FORWARD)书签查询可以用在堆中,也可以用在聚集索引中,正如上所描述的,在SQL Server 2000中,堆上的书签查询与聚集索引上的书签查询是相同的,而在SQL Server 2005中,堆上的书签查询仍旧使用一个嵌套循环联接运算,代替了聚集索引查找,SQL Server使用一个叫做RID查询运算符.RID查询运算符包括在堆上进行书签查询的查找谓词,但是堆并不是一个索引,RID查询也不是一个索引查找.

 

posted on 2009-02-12 14:09  DavidYang  阅读(190)  评论(0编辑  收藏  举报