Oracle 硬解析与软解析

系统 1963 0

--=======================

-- Oracle 硬解析与软解析

--=======================

 

    Oracle 硬解析与软解析是我们经常遇到的问题,什么情况会产生硬解析,什么情况产生软解析,又当如何避免硬解析?下面的描述将给出

软硬解析的产生,以及硬解析的弊端和如何避免硬解析的产生。

   

一、 SQL 语句的执行过程

    当发布一条 SQL PL / SQL 命令时, Oracle 会自动寻找该命令是否存在于共享池中来决定对当前的语句使用硬解析或软解析。

    通常情况下, SQL 语句的执行过程如下:

    a . SQL 代码的语法 ( 语法的正确性 ) 及语义检查 ( 对象的存在性与权限 )

    b . SQL 代码的文本进行哈希得到哈希值。

    c . 如果共享池中存在相同的哈希值,则对这个命令进一步判断是否进行软解析,否则到 e 步骤。

    d . 对于存在相同哈希值的新命令行,其文本将与已存在的命令行的文本逐个进行比较。这些比较包括大小写,字符串是否一致,空格,注释

        等,如果一致,则对其进行软解析,转到步骤 f 。否则到 d 步骤。

    e . 硬解析,生成执行计划。

    f . 执行 SQL 代码,返回结果。

 

二、不能使用软解析的情形    

    1. 下面的三个查询语句,不能使用相同的共享 SQL 区。尽管查询的表对象使用了大小写,但 Oracle 为其生成了不同的执行计划

        select * from emp ;

        select * from Emp ;

        select * from EMP ;

    2. 类似的情况,下面的查询中,尽管其 where 子句 empno 的值不同, Oracle 同样为其生成了不同的执行计划

        select * from emp where empno = 7369

        select * from emp where empno = 7788

 

    3. 在判断是否使用硬解析时,所参照的对象及 schema 应该是相同的,如果对象相同,而 schema 不同,则需要使用硬解析 , 生成不同的执行计划

        sys@ASMDB > select owner , table_name from dba_tables where table_name like 'TB_OBJ%' ;

 

        OWNER                           TABLE_NAME

        ------------------------------ ------------------------------

        USR1                            TB_OBJ                -- 两个对象的名字相同,当所有者不同

        SCOTT                           TB_OBJ

 

        usr1@ASMDB > select * from tb_obj ;

 

        scott@ASMDB > select * from tb_obj ;       -- 此时两者都需要使用硬解析以及走不同的执行计划

 

三、硬解析的弊端

        硬解析即整个 SQL 语句的执行需要完完全全的解析,生成执行计划。而硬解析,生成执行计划需要耗用 CPU 资源,以及 SGA 资源。在此不

    得不提的是对库缓存中闩的使用。闩是锁的细化,可以理解为是一种轻量级的串行化设备。当进程申请到闩后,则这些闩用于保护共享内存

    的数在同一时刻不会被两个以上的进程修改。在硬解析时,需要申请闩的使用,而闩的数量在有限的情况下需要等待。大量的闩的使用由此

    造成需要使用闩的进程排队越频繁,性能则逾低下。

       

四、硬解析的演示        

    下面对上面的两种情形进行演示

    在两个不同的 session 中完成,一个为 sys 帐户的 session ,一个为 scott 账户的 session ,不同的 session ,其 SQL 命令行以不同的帐户名开头

    " sys@ASMDB> "   表示使用时 sys 帐户的 session " scott@ASMDB> " 表示 scott 帐户的 session

 

        sys@ASMDB > select name , class , value from v$sysstat where statistic# = 331 ;         

 

        NAME                       CLASS       VALUE

        -------------------- ---------- ----------            -- 当前的硬解析值为 569

        parse count ( hard )            64         569

 

        scott@ASMDB > select * from emp ;   

 

        sys@ASMDB > select name , class , value from v$sysstat where statistic# = 331 ;      

 

        NAME                       CLASS       VALUE

        -------------------- ---------- ----------            -- 执行上一个查询后硬解析值为 570 ,解析次数增加了一次

        parse count ( hard )            64         570

 

        scott@ASMDB > select * from Emp ;

 

        sys@ASMDB > select name , class , value from v$sysstat where statistic# = 331 ;        

 

        NAME                       CLASS       VALUE

        -------------------- ---------- ----------            -- 执行上一个查询后硬解析值为 571

        parse count ( hard )            64         571

 

        scott@ASMDB > select * from EMP ;

 

        sys@ASMDB > select name , class , value from v$sysstat where statistic# = 331 ;        

 

        NAME                       CLASS       VALUE

        -------------------- ---------- ----------            -- 执行上一个查询后硬解析值为 572

        parse count ( hard )            64         572   

 

        scott@ASMDB > select * from emp where empno = 7369 ;       

 

        sys@ASMDB > select name , class , value from v$sysstat where statistic# = 331 ;

 

        NAME                       CLASS       VALUE

        -------------------- ---------- ----------            -- 执行上一个查询后硬解析值为 573

        parse count ( hard )            64         573

 

        scott@ASMDB > select * from emp where empno = 7369 ;    

 

        sys@ASMDB > select name , class , value from v$sysstat where statistic# = 331 ;

 

        NAME                       CLASS       VALUE

        -------------------- ---------- ----------           -- 执行上一个查询后硬解析值为 574

        parse count ( hard )            64         574

 

    从上面的示例中可以看出,尽管执行的语句存在细微的差别,但 Oracle 还是为其进行了硬解析,生成了不同的执行计划。即便是同样的 SQL

    语句,而两条语句中空格的多少不一样, Oracle 同样会进行硬解析。

 

五、编码硬解析的改进方法

    1. 更改参数 cursor_sharing

        参数 cursor_sharing 决定了何种类型的 SQL 能够使用相同的 SQL area

        CURSOR_SHARING = { SIMILAR | EXACT | FORCE }    

            EXACT       -- 只有当发布的 SQL 语句与缓存中的语句完全相同时才用已有的执行计划。

            FORCE       -- 如果 SQL 语句是字面量,则迫使 Optimizer 始终使用已有的执行计划 , 无论已有的执行计划是不是最佳的。

            SIMILAR     -- 如果 SQL 语句是字面量,则只有当已有的执行计划是最佳时才使用它,如果已有执行计划不是最佳则重新对这个 SQL

                       -- 语句进行分析来制定最佳执行计划。

        可以基于不同的级别来设定该参数,如 ALTER SESSION , ALTER SYSTEM

 

            sys@ASMDB > show parameter cursor_shar              -- 查看参数 cursor_sharing

 

            NAME                                  TYPE         VALUE

            ------------------------------------ ----------- ------------------------------

            cursor_sharing                        string       EXACT

 

            sys@ASMDB > alter system set cursor_sharing = 'similar' ;     -- 将参数 cursor_sharing 的值更改为 similar

 

            sys@ASMDB > select name , class , value from v$sysstat where statistic# = 331 ;    

 

            NAME                       CLASS       VALUE

            -------------------- ---------- ----------         -- 当前硬解析的值为 865

            parse count ( hard )            64         865

 

            scott@ASMDB > select * from dept where deptno = 10 ;

           

            sys@ASMDB > select name , class , value from v$sysstat where statistic# = 331 ;  

 

            NAME                       CLASS       VALUE

            -------------------- ---------- ----------         -- 执行上一条 SQL 查询后,硬解析的值变为 866

            parse count ( hard )             64         866

 

            scott@ASMDB > select * from dept where deptno = 20 ;

 

            sys@ASMDB > select name , class , value from v$sysstat where statistic# = 331 ;

 

            NAME                       CLASS       VALUE

            -------------------- ---------- ----------         -- 执行上一条 SQL 查询后,硬解析的值没有发生变化还是 866

            parse count ( hard )            64         866

 

            sys@ASMDB > select sql_text , child_number from v$sql    -- 在下面的结果中可以看到 SQL_TEXT 列中使用了绑定变量 :"SYS_B_0"

              2   where sql_text like 'select * from dept where deptno%' ;

 

            SQL_TEXT                                            CHILD_NUMBER

            -------------------------------------------------- ------------   

            select * from dept where deptno =: "SYS_B_0"                     0

 

            sys@ASMDB > alter system set cursor_sharing = 'exact' ;        -- cursor_sharing 改回为 exact

 

            -- 接下来在 scott session 中执行 deptno=40 和的查询后再查看 sql_text ,当 cursor_sharing 改为 exact 后,每执行那个一次

            -- 也会在 v$sql 中增加一条语句

 

            sys@ASMDB > select sql_text , child_number from v$sql                

              2   where sql_text like 'select * from dept where deptno%' ;

 

            SQL_TEXT                                            CHILD_NUMBER

            -------------------------------------------------- ------------

            select * from dept where deptno = 50                             0      

            select * from dept where deptno = 40                             0

            select * from dept where deptno =: "SYS_B_0"                     0

 

        注意当该参数设置为 similar ,会产生不利的影响,可以参考这里: cursor_sharing 于expdp

 

    2. 使用绑定变量

        绑定变量要求变量名称,数据类型以及长度是一致,否则无法使用软解析

        绑定变量( bind variable )是指在 DML 语句中使用一个占位符,即使用冒号后面紧跟变量名的形式,如下

            select * from emp where empno = 7788     -- 未使用绑定变量

            select * from emp where empono =: eno    --:eno 即为绑定变量

        在第二个查询中,变量值在查询执行时被提供。该查询只编译一次,随后会把查询计划存储在一个共享池(库缓存)中,以便以后获取

            和重用这个查询计划。

   

        下面使用了绑定变量,但两个变量其实质是不相同的,对这种情形,同样使用硬解析

            select * from emp where empno =: eno ;

            select * from emp where empno =: emp_no

 

        使用绑定变量时要求不同的会话中使用了相同的回话环境,以及优化器的规则等。

 

        使用绑定变量的例子 ( 参照了 TOM 大师的 Oracle 9i & 10g 编程艺术 )

            scott@ASMDB > create table tb_test ( col int );      -- 创建表 tb_test

 

            scott@ASMDB > create or replace procedure proc1   -- 创建存储过程 proc1 使用绑定变量来插入新记录

              2   as

              3   begin

              4       for i in 1..10000

              5       loop

              6           execute immediate 'insert into tb_test values(:n)' using i ;

              7       end loop ;

              8   end ;

              9   /

 

            Procedure created .

 

            scott@ASMDB > create or replace procedure proc2 -- 创建存储过程 proc2 ,未使用绑定变量,因此每一个 SQL 插入语句都会硬解析

              2   as

              3   begin

              4       for i in 1..10000

              5       loop

              6           execute immediate 'insert into tb_test values(' || i|| ')' ;

              7       end loop ;

              8   end ;

              9   /

 

            Procedure created .

 

            scott@ASMDB > exec runstats_pkg . rs_start

 

            PL / SQL procedure successfully completed .

 

            scott@ASMDB > exec proc1 ;

 

            PL / SQL procedure successfully completed .

 

            scott@ASMDB > exec runstats_pkg . rs_middle ;

 

            PL / SQL procedure successfully completed .

 

            scott@ASMDB > exec proc2 ;

 

            PL / SQL procedure successfully completed .

 

            scott@ASMDB > exec runstats_pkg . rs_stop ( 1000 );

            Run1 ran in 1769 hsecs

            Run2 ran in 12243 hsecs              --run2 运行的时间是 run1 /1769≈

            run 1 ran in 14.45 % of the time   

 

            Name                                 Run1       Run2       Diff

            LATCH . SQL memory manager worka        410      2 , 694       2 , 284

            LATCH . session allocation              532      8 , 912      8 , 380

            LATCH . simulator lru latch              33      9 , 371      9 , 338

            LATCH . simulator hash latch             51      9 , 398      9 , 347

            STAT ... enqueue requests                31     10 , 030      9 , 999

            STAT ... enqueue releases                29     10 , 030     10 , 001

            STAT ... parse count ( hard )               4     10 , 011     10 , 007     -- 硬解析的次数,前者只有四次

            STAT ... calls to get snapshot s         55     10 , 087     10 , 032

            STAT ... parse count ( total )             33     10 , 067     10 , 034

            STAT ... consistent gets                247     10 , 353     10 , 106

            STAT ... consistent gets from ca        247     10 , 353     10 , 106

            STAT ... recursive calls             10 , 474     20 , 885     10 , 411

            STAT ... db block gets from cach     10 , 408     30 , 371     19 , 963

            STAT ... db block gets               10 , 408     30 , 371     19 , 963

            LATCH . enqueues                        322     21 , 820     21 , 498     -- 闩的队列数比较

            LATCH . enqueue hash chains             351     21 , 904     21 , 553

            STAT ... session logical reads       10 , 655     40 , 724     30 , 069

            LATCH . library cache pin            40 , 348     72 , 410     32 , 062     -- 库缓存 pin

            LATCH . kks stats                         8     40 , 061     40 , 053

            LATCH . library cache lock              318     61 , 294     60 , 976

            LATCH . cache buffers chains         51 , 851    118 , 340     66 , 489

            LATCH . row cache objects               351    123 , 512    123 , 161

            LATCH . library cache                40 , 710    234 , 653    193 , 943

            LATCH . shared pool                  20 , 357    243 , 376    223 , 019

 

            Run1 latches total versus runs -- difference and pct

            Run1       Run2       Diff      Pct

            157 , 159    974 , 086    816 , 927   16.13 %           --proc2 使用闩的数量也远远多于 proc1 ,其比值是 .13%  

 

            PL / SQL procedure successfully completed .

           

        由上面的示例可知,在未使用绑定变量的情形下,不论是解析次数,闩使用的数量,队列,分配的内存,库缓存,行缓存远远高于绑定

        变量的情况。因此尽可能的使用绑定变量避免硬解析产生所需的额外的系统资源。

       

        绑定变量的优点

            减少 SQL 语句的硬解析,从而减少因硬解析产生的额外开销 ( CPU , Shared pool , latch ) 。其次提高编程效率,减少数据库的访问次数。

        绑定变量的缺点

            优化器就会忽略直方图的信息,在生成执行计划的时候可能不够优化。 SQL 优化相对比较困难

 

六、总结

    1. 尽可能的避免硬解析,因为硬解析需要更多的 CPU 资源,闩等。

    2.cursor_sharing 参数应权衡利弊,需要考虑使用 similar force 带来的影响。

    3. 尽可能的使用绑定变量来避免硬解析。

   

七、更多参考

有关闪回特性请参考

        Oracle 闪回特性(FLASHBACK DATABASE)

Oracle 闪回特性(FLASHBACK DROP & RECYCLEBIN)

Oracle 闪回特性(Flashback Query 、Flashback Table)

Oracle 闪回特性(Flashback Version 、Flashback Transaction)

 

有关基于用户管理的备份和备份恢复的概念请参考:

        Oracle 冷备份

        Oracle 热备份

        Oracle 备份恢复概念

        Oracle 实例恢复

        Oracle 基于用户管理恢复的处理 ( 详细描述了介质恢复及其处理 )

       

    有关 RMAN 的恢复与管理请参考:

        RMAN 概述及其体系结构

        RMAN 配置、监控 与管理

        RMAN 备份详解

        RMAN 还原与恢复

       

    有关 Oracle 体系结构请参考:

        Oracle 实例和Oracle 数据库(Oracle 体系结构)

        Oracle 表空间与数据文件

        Oracle 密码文件

        Oracle 参数文件

Oracle 数据库实例启动关闭过程

        Oracle 联机重做日志文件(ONLINE LOG FILE)

        Oracle 控制文件(CONTROLFILE)

        Oracle 归档日志

   

Oracle 硬解析与软解析


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论