【IT168 技术】在前面的三篇文章(游标脚本性能问题详解之案例实践篇)、(游标脚本性能问题详解之游标分类特点篇 )与(游标脚本性能问题详解之解决方案篇)中我们已经对游标脚本的性能问题做了详细概述,下面让我们来一起了解一下游标的相关知识。
(一)Fast Forward Only(快速只进)游标分析
Microsoft SQL Server 实现了一种称作fast forward only游标的性能优化。http://msdn.microsoft.com/zh-cn/library/ms187502.aspx
当遇到下面情形时,Fast Forward-only游标会隐式转换为其他类型
1. 当SELECT语句连接包含trigger table(INSERTED/DELETED)的一个或多个表,游标被转换成static类型。
2. 当SELECT语句查阅text、ntext或image列,如果SQL Server OLE DB访问接口或使用了SQL Server ODBC驱动器,游标被转换成dynamic类型。
3. 当Fast Forward-only游标不是只读的,它会被转换成dynamic游标。动态游标是可以用来update当前滚动到的数据行并将更新回写到table中的。
4. 当SELECT语句是一个查阅了链接服务器中一个或多个远程表进行分部查询,游标被转换成keyset-driven类型。
5. 如果SELECT语句查阅text、ntext或image列以及TOP语句,游标被转换成keyset-driven类型。
(二)几种游标的使用规则:
另外,我们这里列出了几条游标使用规则可供参考:
1. 如果应用程序中,只能使用服务器端游标(所有在SQL Server上定义的游标都是服务器端游标,如果应用程序在客户端使用游标,在SQL Server端是不会看到有游标打开的),尽量选择使用FORWARD-ONLY、FAST-FORWARD、READ-ONLY游标。当处理单向只读数据时,使用FAST_FORWARD选项而不是FORWARD_ONLY,因为它可以提供一些内部的性能优化。这种类型的所游标产生的SQL Server整体开销是最少的。如果您无法使用FAST_FORWARD游标,可以按序尝试使用下面的游标,找到适合您需求的游标。按照他们的性能特征列出,从最快到最慢为:dynamic,static,keyset。
2. 除非没有其他选择,避免使用static/insensitive和keyset游标,这是因为他们会在TEMPDB中创建临时表,这会增加整体开销导致内存抢夺问题。
3. 使用游标会减少并行而导致不必要的锁和阻塞的发生。要避免这种情况,可以适当地使用READ_ONLY游标选项;或者在你需要进行更新时,使用OPTIMISTIC游标选项来减少锁的产生。避免使用SCROLL_LOCKS游标选项,因为它会减少并行。
4. 从应用程序的角度尽快的加载并且滚动游标到最后一行。这会释放在创建游标时随之创建的共享锁,从而释放SQL Server资源。
5. 如果你的应用程序需要手动滚动记录并进行更新而必须使用游标,请避免使用客户端游标,除非返回的行数很少或数据是静态的。如果行数很大,或者数据不是静态的,可以考虑使用服务器端的keyset游标。由于客户端和服务器端网络拥挤的减少,性能问题很可能会出现。为了优化性能,可能需要在实际情况下对两种游标类型都进行尝试,决定哪种更适合需要应用的系统。
6. 如果游标需要执行JOIN操作,keyset和static游标通常比dynamic游标快。
(三)深入了解Fast_forward游标
1. 既然已经有了read_only forward_only游标,fast_forward游标岂不是是多余的?为什么还需要它们?
fast_forward的确是”多余”的。read_only forward_only游标确实适用于很多应用,但是在有些应用的查询计划中却不理想。Read_only forward_only游标是动态游标,动态游标一般使用的是动态计划。问题在于,有些情况下即使最好的动态计划也远不如静态计划。因此我们引入了Fast_forward游标来使用了一种更平衡的方式,当静态计划更适合时它会选择静态计划。
2. 什么时候用fast_forward,什么时候用read_only forward_only?
综合来说,fast_forward游标更好一点。但是,在做最后决定之前应该先对您的应用进行性能测试。这是因为,使用动态或者静态计划的决定方式是完全不同的(看下面的解释)。不论使用哪种计划方式或哪种游标模式,索引调优(index tuning)或计划提示(plan hint)都会是游标优化的一个重要部分。
3. 什么是动态计划?
动态计划可以增量地进行,在SQL Server中,我们通过将查询执行状态序列化到maker中来实现。然后,我们可以构建一个新的查询计划树,使用刚刚的maker来复位每个操作。另外,动态计划可以根据当前位置前后移动。dynamic和一些fast_forward游标都会使用动态计划。
动态计划只包括动态运算符(支持maker及前后的移动),这很像流操作符中进行查询的notion(stop-and-go)。但并不是每个流操作符都是动态的。
在SQL Server中,动态意味着:
(1) 操作符可以使用maker复位到它当前的位置,或者到当前位置的相关位置(下一个或前一个)
(2) 操作符状态一定要小,从而使maker比较小。操作符中不能存储行数据,尤其是sort table、hash table或者work table,甚至一行都不可以,因为即使单行也可能很大。
没有动态计划,游标可能需要临时存储媒介来保存查询结果集(或它的keyset)。然而,某些操作符对动态执行计划来说是不合适的,比如hash join、hash agg、compute sequence和sort。这会导致一个次优的计划。
4. 什么时候动态计划会不如静态计划?
在某些查询中,比如使用row_number的,动态计划是不可用的。但是当动态静态都可用时就有问题出现了:在某个操作符(比如join)既有动态(nested loops)也有非动态(hash)实现时可能发生;当有些索引支持排序而有些不支持时,也可能会发生问题。
下面是一个例子,表ORDERS有一个DATE上的索引,以及一个SUBTOTAL上的索引(在这个例子中,clustered或者non-clustered没关系)。该查询想要查询订单信息:
SELECTDATE, SUBTOTAL, ORDERID, CUSTOMERID
FROM ORDERS where SUBTOTAL > 10000000
ORDERBYDATE
表中有一亿条数据,查询结果包含100条。动态游标不能排序,所以它必须使用DATE索引,查看每条数据,填到SUBTOTAL中。而静态游标可以查询大于10000范围的SUBTOTAL索引,排序并把它们存储到游标(temp table)中。
5. fast_forward游标如何解决该问题?
在特定条件下,fast_forward游标从非常好的静态计划和非常好的动态计划中选择开销最小的一个。在上面所提到的极端例子中,它就会使用静态计划。
6. 应用开发者可以做什么?
(1) 为OPTION(FAST )选择一个中性值。
(2) 通过调用sp_cursorprepare或OPTION(RECOMPILE)避免在不同参数下的计划重用。
(3) 避免在游标中使用ORDER BY
(4) 使用相等谓词及多列索引来支持序列。稍微修改一下上面的例子,如果我们在SUBTOTAL范围有一个包含值{S,M,L,XL}的计算列SIZE,我们可以查询WHERE SIZE=’XL’ ORDER BY DATE,并在上使用索引。通常通过增加索引列来包括相等谓词后的排序列。