Oracle的硬解析和软解析
提到软解析(soft prase)和硬解析(hard prase),就不能不说一下Oracle对sql的处理过程。当你发出一条sql语句交付Oracle,在执行和获取结果前,Oracle对此sql将进行几个步骤的处理过程: 1、语法检查(syntax check) 检查此sql的拼写是否语法。 2、语义检查(semantic check) 诸如检查sql语句中的访问对象是否存在及该用户是否具备相应的权限。 3、对sql语句进行解析(prase) 利用内部算法对sql进行解析,生成解析树(parse tree)及执行计划(execution plan)。 4、执行sql,返回结果(execute and return) 其中,软、硬解析就发生在第三个过程里。 创建解析树、生成执行计划对于sql的执行来说是开销昂贵的动作,所以,应当极力避免硬解析,尽量使用软解析。 |
当客户端进程,将SQL语句通过监听器发送到Oracle时, 会触发一个Server process生成,来对该客户进程服务。Server process得到SQL语句之后,对SQL语句进行Hash运算,然后根据Hash值到library cache中查找,如果存在,则直接将library cache中的缓存的执行计划拿来执行,最后将执行结果返回该客户端,这种SQL解析叫做软解析;如果不存在,则会对该SQL进行解析parse,然后执行,返回结果,这种SQL解析叫做硬解析。
1.硬解析的步骤
硬解析一般包括下面几个过程:
1)对SQL语句进行语法检查,看是否有语法错误。比如select from where 等的拼写错误,如果存在语法错误,则推出解析过程;
2)通过数据字典(row cache),检查SQL语句中涉及的对象和列是否存在。如果不存在,则推出解析过程。
3)检查SQL语句的用户是否对涉及到的对象是否有权限。如果没有则推出解析;
4)通过优化器创建一个最优的执行计划。这个过程会根据数据字典中的对象的统计信息,来计算多个执行计划的cost,从而得到一个最优的执行计划。这一步涉及到大量的数据运算,从而会消耗大量的CPU资源;(library cache最主要的目的就是通过软解析来减少这个步骤);
5)将该游标所产生的执行计划,SQL文本等装载进library cache中的heap中。
2.软解析
所谓软解析,就是因为相同文本的SQL语句存在于library cache中,所以本次SQL语句的解析就可以去掉硬解析中的一个活多个步骤。从而节省大量的资源的耗费。
shared pool的组成:
3块区域:free、librarycache、row cache(dictionary cache)
library cache:缓存SQL语句以及SQL语句的执行计划
dictionary cache:Oracle数据库自身的信息(数据库中有多少表、多少用户、表有多少列、列名是什么、列的数据类型、每个表多大)存放在dictionary中
数据字典举例:想知道数据库中有没有T1表
1、create table t1 as select * from dba_objects;
2、desc dba_tables; —————数据字典信息表
3、select table_name,owner from dba_tables where table_name like ‘T1%’;
所有数据字典信息可在官方文档中查找booksàreferenceàdba_tables
1、查看librarycache大小
select * from v$sgastat a where a.NAME = ‘library cache’;
2、free空间大小
select * from v$sgastat a where a.pool = ‘shared pool’ anda.NAME = ‘free memory’;
3、row cache空间大小
select * from v$sgastat a where a.NAME = ‘row cache’;
什么时候发生硬解析:
Server process 拿着SQL语句在librarycache中找,如果这条SQL语句在library cache中没有,说明这条SQL语句和他的执行计划在library cache中没有——-硬解析
有——–软解析
无论hard parse还是soft parse,解析过程中用到很多数据库自身信息(权限信息、对象信息、对象统计信息——字典信息);即对SQL语句进行解析(软硬)的时候,都要频繁的访问数据字典信息———-所以row cache放在shared pool和library cache在一起
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; www.2cto.com
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能够使用相同的SQLarea
CURSOR_SHARING = { SIMILAR | EXACT | FORCE }
EXACT –只有当发布的SQL语句与缓存中的语句完全相同时才用已有的执行计划。
FORCE –如果SQL语句是字面量,则迫使Optimizer始终使用已有的执行计划,无论已有的执行计划是不是最佳的。
SIMILAR –如果SQL语句是字面量,则只有当已有的执行计划是最佳时才使用它,如果已有执行计划不是最佳则重新对这个SQL
–语句进行分析来制定最佳执行计划。
可以基于不同的级别来设定该参数,如ALTER SESSION, ALTER SYSTEM
sys@ASMDB> show parametercursor_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,会产生不利的影响
2.使用绑定变量
绑定变量要求变量名称,数据类型以及长度是一致,否则无法使用软解析
绑定变量(bindvariable)是指在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 110000
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 110000
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);
Run1ran in 1769 hsecs
Run2ran in 12243hsecs –run2运行的时间是run1的/1769≈倍
run1 ran in 14.45% of the time
Name Run1 Run2 Diff
LATCH.SQL memory managerworka 410 2,694 2,284
LATCH.sessionallocation 532 8,912 8,380
LATCH.simulator lrulatch 33 9,371 9,338
LATCH.simulator hashlatch 51 9,398 9,347
STAT…enqueuerequests 31 10,030 9,999
STAT…enqueuereleases 29 10,030 10,001
STAT…parse count (hard) 4 10,011 10,007 –硬解析的次数,前者只有四次
STAT…calls to get snapshots 55 10,087 10,032
STAT…parse count (total) 33 10,067 10,034
STAT…consistentgets 247 10,353 10,106
STAT…consistent gets from ca 247 10,353 10,106
STAT…recursivecalls 10,474 20,885 10,411
STAT…db block gets from cach 10,408 30,371 19,963
STAT…db blockgets 10,408 30,371 19,963
LATCH.enqueues 322 21,820 21,498 –闩的队列数比较
LATCH.enqueue hashchains 351 21,904 21,553
STAT…session logical reads 10,655 40,724 30,069
LATCH.library cachepin 40,348 72,410 32,062 –库缓存pin
LATCH.kksstats 8 40,061 40,053
LATCH.library cachelock 318 61,294 60,976
LATCH.cache bufferschains 51,851 118,340 66,489
LATCH.row cacheobjects 351 123,512 123,161
LATCH.librarycache 40,710 234,653 193,943
LATCH.sharedpool 20,357 243,376 223,019
Run1latches 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中每条SQL语句在执行之前都需要经过解析,这里面又分为软解析和硬解析。在Oracle中存在两种类型的SQL语句,一类为 DDL语句(数据定义语言),他们是从来不会共享使用的,也就是每次执行都需要进行硬解析。还有一类就是DML语句(数据操纵语言),他们会根据情况选择要么进行硬解析,要么进行软解析。
DML:INSERT,UPDATE,DELETE,SELECT
DDL:CREATE,DROP,ALTER
一. SQL 解析过程
Oracle对此SQL将进行几个步骤的处理过程:
1、语法检查(syntax check): 检查此sql的拼写是否语法。
2、语义检查(semantic check): 诸如检查sql语句中的访问对象是否存在及该用户是否具备相应的权限。
3、对sql语句进行解析(prase): 利用内部算法对sql进行解析,生成解析树(parse tree)及执行计划(execution plan)。
4、执行sql,返回结果(execute and return)
二. 解析过程详解
2.1 语法检测
判断一条SQL语句的语法是否符合SQL的规范,比如执行:
SQL> selet * from emp;
我们就可以看出由于Select关键字少了一个“c”,这条语句就无法通过语法检验的步骤了。
2.2 语义检查
语法正确的SQL语句在解析的第二个步骤就是判断该SQL语句所访问的表及列是否准确?用户是否有权限访问或更改相应的表或列? 比如如下语句:
SQL> select * from emp;
select * from emp
*
ERROR at line 1:
ORA-00942: table or view does not exist
由于查询用户没有可供访问的emp对象,因此该SQL语句无法通过语义检查。
2.3 解析(Parse)
2.3.1 Parse主要分为三种:
1、Hard Parse (硬解析)
2、Soft Parse (软解析)
3、Soft Soft Parse(好像有些资料中并没有将这个算在其中)
Hard Parse: 就是上面提到的对提交的Sql完全重新从头进行解析(当在Shared Pool中找不到时候将会进行此操作),总共有一下5个执行步骤:
1:语法分析
2:权限与对象检查
3: 在共享池中检查是否有完全相同的之前完全解析好的. 如果存在,直接跳过4和5,运行Sql, 此时算soft parse.
4:选择执行计划
5:产生执行计划
注:创建解析树、生成执行计划对于sql的执行来说是开销昂贵的动作,所以,应当极力避免硬解析,尽量使用软解析。这就是在很多项目中,倡导开发设计人员对功能相同的代码要努力保持代码的一致性,以及要在程序中多使用绑定变量的原因。
Soft Parse: 就如果是在Shared Pool中找到了与之完全相同的Sql解析好的结果后会跳过Hard Parse中的后面的两个步骤。
Soft Soft Parse: 实际上是当设置了session_cursor_cache这个参数之后,Cursor被直接Cache在当前Session的PGA中的,在解析的时候只需要对其语法分析、权限对象分析之后就可以转到PGA中查找了,如果发现完全相同的Cursor,就可以直接去取结果了,也就就是实现了 Soft Soft Parse.