详解ORACLE中游标的生命周期

来源:互联网 发布:淘宝客服哪里培训课程 编辑:程序博客网 时间:2024/06/04 19:05

在网络或者书籍中,我们可以非常容易的了解到ORACLE中游标的生命周期包括如下部分:

1,打开游标-- open cursor,此步骤在 UGA 里申请一块内存给游标使用,这个时候游标还没有与sql语句关联。
2,解析游标-- sql与游标关联起来,解析sql的内容(包括执行计划),解析后的内容会被加载到共享池中(share pool-- library cache)。在UGA申请的内存用来保存指向这个共享游标(share cursor)在library cache中的位置。
3,定义输出变量-- 如果sql语句返回数据,必须先定义接收数据的变量。这一点不仅对查询语句很重要,对于使用returning 自居的delete、insert和update 语句也很重要。
4,绑定输入变量-- 如果sql语句使用了绑定变量,必须提供他们的值。绑定的过程是不做什么检查。如果指定了无效的数据,执行的过程中会爆出一个运行时错误。
5,执行游标-- 执行跟游标关联的sql。注意 数据库并非总是在这一步做重要的事情。事实上,对于很多类型的查询语句来说,真正的处理过程通常会被推迟到fetch数据阶段。
6,获取游标-- 如果sql语句返回数据,这一步会接受这些数据。特别是在查询语句中,大部分的处理工作都是在这一步进行的。在查询语句中,可能只会读取部分记录,换句话讲,游标有可能在取到所有记录前被关闭。
7,关闭游标-- 释放UGA中与这个游标有关的资源,从而这些资源可供其他的游标使用。在library cache中的share cursor不会被清除,它会继续保留在library cache 中,等待被重用(软解析重用)。


重复的内容,我们不做过多介绍,今天我们来看一下游标生命周期中各个部分所扮演的角色,以及如何利用它们来优化我们的程序。

借鉴《oracle性能诊断艺术》中的代码片段,我们来研究一下游标的生命周期;

create or replace procedure cursor_test as  l_ename  emp.ename%TYPE := 'SCOTT';  l_empno  dbms_sql.Number_Table;  l_cursor INTEGER;  l_retval INTEGER;  cnt integer := 1;  indx integer := 1;  res varchar2(4000);BEGIN  for i in 1 .. 1000 loop    l_cursor := DBMS_SQL.open_cursor;    DBMS_SQL.parse(l_cursor,                   'select empno from emp where ename <> :ename and 0 <> '||i , DBMS_SQL.native);    l_empno.delete();    DBMS_SQL.define_array(l_cursor, 1, l_empno,cnt,indx);    DBMS_SQL.bind_variable(l_cursor, ':ename', to_char(i));    l_retval := DBMS_SQL.execute(l_cursor);    while DBMS_SQL.fetch_rows(l_cursor) > 0 loop      dbms_sql.column_value(l_cursor, 1, l_empno);    end loop;    res :='';    for j in 1 .. l_empno.count() loop        res := res || L_EMPNO(j);     end loop;     DBMS_OUTPUT.PUT_LINE(res);    dbms_sql.close_cursor(l_cursor);  end loop;end;

通过plsql profiler跟踪各行代码的执行效率如下:



可以看出,游标生命周期中的各个部分均会占有执行时间,因此,如果可以消除一些执行步骤显然会提高性能. 那些操作可以消除那?参加下图

--                      -----------  --                    | open_cursor |  --                      -----------  --                           |  --                           |  --                           v  --                         -----  --          ------------>| parse |  --         |               -----  --         |                 |  --         |                 |---------  --         |                 v         |  --         |           --------------  |  --         |-------->| bind_variable | |  --         |     ^     -------------   |  --         |     |           |         |  --         |      -----------|         |  --         |                 |<--------  --         |                 v  --         |               query?---------- yes ---------  --         |                 |                           |  --         |                no                           |  --         |                 |                           |  --         |                 v                           v  --         |              -------                  -------------  --         |----------->| execute |            ->| define_column |  --         |              -------             |    -------------  --         |                 |------------    |          |  --         |                 |            |    ----------|  --         |                 v            |              v  --         |           --------------     |           -------  --         |       ->| variable_value |   |  ------>| execute |  --         |      |    --------------     | |         -------  --         |      |          |            | |            |  --         |       ----------|            | |            |  --         |                 |            | |            v  --         |                 |            | |        ----------  --         |                 |<-----------  |----->| fetch_rows |  --         |                 |              |        ----------  --         |                 |              |            |  --         |                 |              |            v  --         |                 |              |    --------------------  --         |                 |              |  | column_value         |  --         |                 |              |  | variable_value       |  --         |                 |              |    ---------------------  --         |                 |              |            |  --         |                 |<--------------------------  --         |                 |  --          -----------------|  --                           |  --                           v  --                      ------------  --                    | close_cursor |  --                      ------------  --

首先,将游标的打开和关闭修改为只执行一次,效果如下


再来将解析消除,首先清空共享池,看看游标解析是否消耗大量资源


可以看出,解析还是消耗资源的,尤其是硬解析时,同时也可以看到open_cursor消耗的时间剧增,因此不到万不得已,千万不要清空共享池.如果要消除解析,那么每次执行的语句必须是一致的,可以通过绑带变量的方式来实现,修改程序代码如下

create or replace procedure cursor_test as  l_ename  emp.ename%TYPE := 'SCOTT';  l_empno  dbms_sql.Number_Table;  l_cursor INTEGER;  l_retval INTEGER;  cnt integer := 1;  indx integer := 1;  res varchar2(4000);BEGIN  l_cursor := DBMS_SQL.open_cursor;  DBMS_SQL.parse(l_cursor,                   'select empno from emp where ename <> :ename' , DBMS_SQL.native);  for i in 1 .. 1000 loop    l_empno.delete();    DBMS_SQL.define_array(l_cursor, 1, l_empno,cnt,indx);    DBMS_SQL.bind_variable(l_cursor, ':ename', to_char(i));    l_retval := DBMS_SQL.execute(l_cursor);    while DBMS_SQL.fetch_rows(l_cursor) > 0 loop      dbms_sql.column_value(l_cursor, 1, l_empno);    end loop;    res :='';    for j in 1 .. l_empno.count() loop        res := res || L_EMPNO(j);     end loop;     DBMS_OUTPUT.PUT_LINE(res);  end loop;   dbms_sql.close_cursor(l_cursor);end;

执行效率如下:


下面我们来优化最耗时的部分,通过增加cnt的取值,(类似于SQLPLUS中的set arraysize )


效率有提高了很多.


总结:理解游标的执行过程,尽可能多的重复各个子环节可以有效提高效率.



1 0
原创粉丝点击