PLSQL_性能优化系列06_Oracle Soft Parse / Hard Parse软硬解析

2014-08-11 BaoXinjian

一、摘要



Oracle硬解析和软解析是我们经常遇到的问题,所以需要考虑何时产生软解析何时产生硬解析,如何判断

1. SQL的执行过程

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

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

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

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

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

Step4. 对于存在相同哈希值的新命令行,其文本将与已存在的命令行的文本逐个进行比较。

这些比较包括大小写,字符串是否一致,空格,注释等,如果一致,则对其进行软解析,转到步骤Step6,无需再次硬解析。

否则到步骤Step5。

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

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

2. Oracle对此sql将进行几个步骤的处理过程:

Step1. 语法检查(syntax check)

  检查此sql的拼写是否语法。

Step2. 语义检查(semantic check)

  诸如检查sql语句中的访问对象是否存在及该用户是否具备相应的权限。

Step3、对sql语句进行解析(parse)

  利用内部算法对sql进行解析,生成解析树(parse tree)及执行计划(execution plan)。

Step4、执行sql,返回结果(execute and return)

3. 硬解析的危害:

(1) 占用资源更多,执行慢,因为不会重用已解析好的query plan。

(2) 硬解析导致library cache上的latch竞争,这会降低系统的并发性,使oracle无法充分利用系统资源。(此时即使系统资源看上去不忙,oracle也会很慢)。

(3) 一个有很多硬解析的简单应用可能导致数据库所有应用变慢。

4. 总结

其中,软、硬解析就发生在第三个过程里(对sql语句进行解析parse)。

  Oracle利用内部的hash算法来取得该sql的hash值,然后在library cache里查找是否存在该hash值;

  假设存在,则将此sql与cache中的进行比较;

  假设“相同”,就将利用已有的解析树与执行计划,而省略了优化器的相关工作。这也就是软解析的过程。

  诚然,如果上面的2个假设中任有一个不成立,那么优化器都将进行创建解析树、生成执行计划的动作。这个过程就叫硬解析。

  创建解析树、生成执行计划对于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资源。在此不得不提的是对库缓存中 闩的使用。闩是锁的细化,可以理解为是一种轻量级的串行化设备。当进程申请到闩后,则这些闩用于保护共享内存的数在同一时刻不会被两个以上的进程修改。在 硬解析时,需要申请闩的使用,而闩的数量在有限的情况下需要等待。大量的闩的使用由此造成需要使用闩的进程排队越频繁,性能则逾低下。

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

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

如" [email protected]> "  表示使用时sys帐户的session," [email protected]> "表示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=7788;   --此处原来empno=7369,复制错误所致,现已更正为[email protected]   

        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_NUMBE
            -------------------------------------------------- ------------
            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

2. 使用绑定变量的方式

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

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

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

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

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

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

select * from emp where empno=:eno;

select * from emp where empno=:emp_no

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

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.

(3). 使用绑定变量的好处

  • 由上面的示例可知,在未使用绑定变量的情形下,不论是解析次数,闩使用的数量,队列,分配的内存,库缓存,行缓存远远高于绑定
  • 变量的情况。因此尽可能的使用绑定变量避免硬解析产生所需的额外的系统资源。
  • 绑定变量的优点
  • 减少SQL语句的硬解析,从而减少因硬解析产生的额外开销(CPU,Shared pool,latch)。其次提高编程效率,减少数据库的访问次数。
  • 绑定变量的缺点
  • 优化器就会忽略直方图的信息,在生成执行计划的时候可能不够优化。SQL优化相对比较困难

五、总结



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

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

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

********************作者: 鲍新建********************

参考:乐沙弥大神 http://blog.csdn.net/leshami/article/details/6195483

http://10.61.208.50:15871/cgi-bin/blockpage.cgi?ws-session=18446744072512592920

http://czmmiao.iteye.com/category/143940

时间: 2024-10-25 00:29:28

PLSQL_性能优化系列06_Oracle Soft Parse / Hard Parse软硬解析的相关文章

PLSQL_性能优化系列16_Oracle DataScan数据扫描

对数据的读取操作是非常消耗资源的,如何减少对数据的扫描,是提升sql效率的一个重要方面,例如物化视图技术.本篇介绍几种sql写法,分别是CASE expression/DML with returning clause /multitable insert.[@[email protected]] 一. 用CASE EXPRESSION将多句查询组合在一起SELECT COUNT (*)FROM employeesWHERE salary < 2000;SELECT COUNT (*)FROM

PLSQL_性能优化系列07_Oracle Parse Bind Variables解析绑定变量

2014-09-25 BaoXinjian 一.绑定变量用法和使用场合 使用绑定变量的重要性:如果不使用绑定变量而使用常量,会导致大量硬解析.由于硬解析的种种危害,不使用绑定变量往往是影响oracle性能和扩展性的最大问题 以下为一些错误写法和正确写法的例子 1. PLSQL中普通查询 (1). 错误写法 SELECT * FROM emp WHERE empno=123; (2). 正确写法(未使用绑定变量) Empno:=123;SEELCT* FROM emp WHERE empno=:e

PLSQL_性能优化系列01_Oracle Index索引

2014-06-01 BaoXinjian 一.摘要 在PLSQL查询优化中,使用和接触最多的应该是索引Index这个概念,个人也觉得对Index选择和优化是程式优化过程中比较重要的概念,特别是刚开始接触PLSQL性能优化 索引的一些概念 一个索引可以由一个或多个列组成, 对列设置索引其实就是对列的内容按一定的方式进行排序,检索数据的时候,检索排过序的数据,检索到最后一个有效数据之后就跳出检索 这样就不必进行全表扫描了,同时可以应用很多算法提高检索效率 数据库多用二分法检索数据 索引的连接方式

PLSQL_性能优化系列15_Oracle Connection Management连接管理

2014-09-25 BaoXinjian 一.摘要 在官方文档<oracle performance tuning guide>中提到Connecting to the database is an expensive operation that is highly unscalable. 数据库的连接操作是昂贵的,且难以扩展(支持大量并发). 感觉上一个数据库登录操作是瞬间的事,它有多昂贵呢? 简单说,监听器收到远程连接请求后,转给server process: 对于每个session数

PLSQL_性能优化系列04_Oracle Optimizer优化器

2014-09-25 BaoXinjian 一.摘要 1. Oracle优化器介绍 本文讲述了Oracle优化器的概念.工作原理和使用方法,兼顾了Oracle8i.9i以及最新的10g三个版本.理解本文将有助于您更好的更有效的进行SQL优化工作. 2. RBO优化器 RBO是一种基于规则的优化器,随着CBO优化器的逐步发展和完善,在最新的10g版本中Oracle已经彻底废除了RBO. 正在使用Oracle8i或9i的人们或多或少的都会碰到RBO,因此在详细介绍CBO之前,我们有必要简单回顾一下古

PLSQL_性能优化系列08_Oracle Insert / Direct Insert性能优化

2014-09-25 BaoXinjian 一.Insert 性能影响 应用设计不合理导致的session之间的互锁(enqueue)是影响程序可扩展性最常见的原因.此外,一些共享资源的争用,也会导致性能下降. 本篇介绍两个由并发insert操作导致的等待事件(wait event),以及如何通过优化物理设计来进行改善. 普通Insert操作本身产生的是行锁,因此进程相互之间不会锁住(enqueue),但当很多进程insert同一张表时,会有资源上冲突. 以下是两个例子: 1. Buffer b

PLSQL_性能优化系列02_Oracle Join关联

2014-09-25 BaoXinjian 一.摘要 Oracle三种主要连接方式的比较 1. Hash Join (1).概述 i. 读取一个表的资料,并将放置到内存中,并建立唯一关键字的位图索引 ii. 读取另一个表,和内存中表通过Hash算法进行比较 (2).适用对象 i. 大表连接小表 ii. 两个大表 2. Nested Loops (1).概述 i. 循环外表记录 ii. 进行逐个比对和内标的连接是否符合条件 (2).适用对象 小表驱动大表,返回较少的结果集 3. Merge Joi

PLSQL_性能优化系列12_Oracle Connection Management

2014-09-25 BaoXinjian 一.摘要 在官方文档<oracle performance tuning guide>中提到Connecting to the database is an expensive operation that is highly unscalable. 数据库的连接操作是昂贵的,且难以扩展(支持大量并发). 感觉上一个数据库登录操作是瞬间的事,它有多昂贵呢? 简单说,监听器收到远程连接请求后,转给server process: 对于每个session数

PLSQL_性能优化系列05_Oracle Hint提示

2014-06-20 BaoXinjian 一.摘要 手工指定SQL语句的执行计划 尽管oracle优化器很智能,但有时候你想自己选择执行计划,可以通过hint实现.在开发测试环境中,可以通过hint测试不同执行计划的性能. Hint的缺点是增加了管理代码的额外负担,当数据库或环境发生变化时,如果不修改hint,可能导致性能下降.例如,代码中用hint指定索引,但重建索引时索引名变化. 因此oracle建议使用hint测试性能后,用其他工具来管理执行计划,如oracle 10g以后的sql tu