理解AWR性能报告中的Execute to Parse%指标

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ZengMuAnSha/article/details/60773621

这个是我生存库的指标,感觉指标好低啊! 这个指标的意思是说 执行次数与解析次数的对比. 按这个意思来说,如果解析1次执行1次 ,这个比例应该为100%。
带着这个理解,解析1次执行1次基本上算是硬解析。而我的系统里面绑定变量的SQL也蛮多的啊,这个指标应该高于100%。
这个指标公式 Execute to Parse %: dscr , round(100*(1-:prse/:exe),2) pctval
比如SQL解析100次,执行150次,换算过来就是1-100/150=1-66.7约 23%; 如SQL解析100次,执行10000次,换算是1-100/10000=1-0.01=99%;
如果是解析1次执行1次的话 100*(1-1/1)=0%
原来标准值是0%,高于这个值的才算是好的系统,它无限接近于100%。 看来是我的大脑的误解!
我们看下解析过程如图

这里写图片描述

解析是什么鬼? 解析也可以说是编译。所谓编译就是把我们人写的字符程序,转换成计算机能执行的二进制代码,也就是101000111。在大学的计算机科班专业的同学,一定学过编译原理这门课。知道编译要做很多事情! 做过开发的人也知道,当项目代码约多,编译速度越慢。就算一个简单的小项目编译一次也要个1分钟的时间。何况是数据块服务器,每天光一条SQL都要执行上万次,一天要执行上千乃至上万条不同的SQL语句。要是每条语句编译一次消耗1秒时间,那么1亿次,需要2万7777小时。需要多少个强劲的CPU才能一内完成编译工作呢? CPU除了编译难道就不要做其他工作了吗?
所以节约编译时间是头等大事! Execute to Parse 这个指标就是衡量编译节约时间的。如果执行一次编译一次就0%值!!

好了 我们想下如何节约编译! 1 如果一条SQL语句 一样的 比如测试数据库是否存活的语句,select sysdate from dual; 这个语句应用程序每个1分钟执行一次,一天要执行360次.
如果每次编译消耗1秒,就无所谓啦.懒得理它呢. 既然它要执行那么多次,我们是否可以把它缓存起来呢? 暂时放在内存里, 里面包含SQL语句和对应的执行计划,也就是编译后的二进制代码. 这样就好了下次再来,就从内存中把它给取出来执行就是了.
内存缓存这个办法 对应上面的图的黄色路径.也就是从PGA白色箭头 到SGA里面的SQL执行计划管理区, 通过黄色箭头 再到SQL执行计划缓存区. 最后就直接执行.

为了提供内存缓存办法的命中率,也就是说除了同样一条语句执行次数外,我们要让类似的语句也缓存起来.这样就提高了命中率.
比如上面说的SQL语句 select sysdate from dual; 这样的语句太特别了,属于特例. 实际中我们不可能出现 重复执行 这样的 select 身份证ID from 全国身份证信息表 where 姓名=’小凡仙’ . 这样的语句我们顶多执行5-6次. 大量的确实这样的类似语句
select 身份证ID from 全国身份证信息表 where 姓名=’小凡仙’;
select 身份证ID from 全国身份证信息表 where 姓名=’中凡仙’;
select 身份证ID from 全国身份证信息表 where 姓名=’大凡仙’;
select 身份证ID from 全国身份证信息表 where 姓名=’张果老’;
select 身份证ID from 全国身份证信息表 where 姓名=’南海观音’;
select 身份证ID from 全国身份证信息表 where 姓名=’东海龙王’;

这样的语句在我们人类看来是一条,而JAVA代码里也是写的一条.可在计算机眼里是不一样的SQL。唯一区别就是查找的姓名的条件不一样而已。然后我们再对比它们的执行计划也是一样的。
如果有什么办法把这些类似的语句,变成一条,尤其是在计算机眼里认为是一条,然后从内存在拿到已经编译好的执行计划。
在计算机眼里怎么认为它是呢? 在ORACLE数据库它这样认为的,先给你发来的SQL字符串用HASH函数转换成数字序列。比如说13345670346489435432435. 然后跟内存中的对比下。 也就是说两个类似的SQL语句 之间 任何个字符不一样就是不同的,比如说 多了个空格,多了个字符,这个字符是小写的。
所以 我们把类似语句不一样的地方,变成一样的。如下,把上面6条类似的用一个英文单词来替代不一样的地方。
select 身份证ID from 全国身份证信息表 where 姓名=:Name;

这就是传说中的绑定变量 不过这个名词有点拗口,也有点晦涩。对于有过开发,或者说编过程的人来讲,不太好理解。如果说是函数的参数化 就比较好理解。
例如写个函数
void call_my_iphone(iphone_num:number) { ……};
调用的时候
call_my_iphone(13828876883);

我们上面那样子写 如同写了个函数. 然后再绑定参数进去.
实际上参数在SQL执行的时候,也就是最后一步才把参数带进去.
这样我们把大部分的SQL语句的编译后的执行计划给缓存起来了.同时提高了缓存中的命中率.
这个绑定变量的方法有个缺陷,不是所有的语句可以采用这个方法的. 这个方法的前提是 类似的语句必须拥有相同的执行计划.

如果缓存了很多SQL语句的执行计划,比如说1万条的话,我们发现执行计划的管理区将是个严重的负担。 ORACLE 采用多个链条 这个数大概是个内部值。也序是128根链条。你可以想象是晒衣服的绳子。这128个绳子上均匀的晒了79个衣服。 我们要找到其中所需要的衣服来穿,我们太懒了,衣服多的要死,直接挂在外面晒,同时又当衣柜。不会下雨的,因为外面全是玻璃罩着。当然我们人眼立刻定位到所需要的衣服。其实我们人眼也要从绳子一头扫描到绳子一尾。 如果这个绳子晒的不同的衣服,不同的颜色,不同款式,就没有这个必要了。然并卵,衣服已经把相同的款式,相同的颜色晒到一个绳子上。 这个怎么可能啊? 不! 这是公共晒衣兼衣柜,除了你,你住的邻居们都在晒。这下好了,一条绳子上除了你的,还有别人的,唯一区别就是衣架上有你的名字。所以你必须从绳头扫描到过去。
好了,我们再想象下,到了早上,大家都要去拿衣服来穿,晒衣区不够宽,这个区尽可能的区域用于晒衣服。早上有10个男人同时要拿白色的存衣来穿。可见他们的衣服被晒在一个绳子上。如果这10个是低素质的男人,他们会一起上去抢,然后打打出手。所以安排了LOCK管理员,LATCH管理员,MUTEX管理员,然后给他们发牌子一个个进去拿。
三个管理员 要处理1万个人拿衣服的请求,自然也负担很重了。除了拿衣服有长有短的时间外,人多了就有个排队领牌子的时间,这个令牌子的时间随着人多,时间就越长。

你想我给管理员塞点钱,沟通下。今天领到这个牌子,是否可以我留着用啊?我保证我的白色寸衣挂在某个晒衣绳子,第56个衣架上。 管理员说也行,不过牌子是留给你不好,别人会说的,我给你搞个绿色通道,VIP卡。卡上说明你的白色寸衣地址。
这就是上图上的绿色箭头。传说中的软软解析

上图左边的PGA 有两个参数 分别是OPEN_CURSOR 和SESSION_CACHE_CURSOR。表示每个人可以开多少个VIP卡。一次使用多少张VIP卡。比如说每个可以开20张,同时只能使用5张卡。

要获得VIP卡,必须跟管理员搞好关系,塞点钱贿赂下。 我们ORACLE 通过会话,执行了3次才有资格。前提必须有空位。一个会话是否会执行3次以上呢? JAVA搞得连接池的目的是减少连接的申请,频繁的申请会导致数据库LINUX进程的工作量。它只是为了这个目的而已,叫连接重用。 在ORACLE 基本上一个连接算是一个会话,当这个连接进程会被随机分配执行不同的功能,因此重复执行一样SQL语句的机会比较低。

查看当前系统session配置

Select 'session_cached_cursors' Parameter,
    Lpad(Value, 5) Value,
    Decode(Value, 0, ' n/a', To_Char(100 * Used / Value, '990') || '%') Usage
From (Select Max(s.Value) Used
            From V$statname n, V$sesstat s
           Where n.Name = 'session cursor cache count'
           And s.Statistic# = n.Statistic#),
    (Select Value From V$parameter Where Name = 'session_cached_cursors')
Union All
Select 'open_cursors',
    Lpad(Value, 5),
    To_Char(100 * Used / Value, '990') || '%'
From (Select Max(Sum(s.Value)) Used
      From V$statname n, V$sesstat s
     Where n.Name In
        ('opened cursors current', 'session cursor cache count')
      And s.Statistic# = n.Statistic#
     Group By s.Sid),
    (Select Value From V$parameter Where Name = 'open_cursors');
PARAMETER              VALUE    USAGE
---------------------- ------- -----

session_cached_cursors 50 98% --当前的使用率为98%,应考虑增加该参数值 open_cursors          300 20% --当前open_cursors仅为20%,说明当前够用
========================================================================

影响EXE/PARSE的指标有如下方面
1 JAVA方面的连接池的连接通道分配算法不够好
2 JAVA 连接池动态调整,比如说减少连接数。从100减少到50. 连接数的减少,意味着会话被终止了。就无法缓存VIP卡。
3 没有被绑定变量,命中率很低。只能硬解析
4 内存大小的变化,因为有个自动内存的管理,会逼迫执行计划缓存区减少,自然要释放掉部分缓存内容。
5 上线,一个应用系统经常会被版本更新,每次更新就会代来新的SQL,挤掉旧的SQL。

– 也可以通过下面的脚步查看cursor的使用情况

SQL> SELECT MAX(A.VALUE) AS HIGHEST_OPEN_CUR, P.VALUE AS MAX_OPEN_CUR  
  2   FROM V$SESSTAT A, V$STATNAME B, V$PARAMETER P  
  3  WHERE A.STATISTIC# = B.STATISTIC#  
  4    AND B.NAME = 'opened cursors current'  
  5    AND P.NAME = 'open_cursors'  
  6  GROUP BY P.VALUE;
HIGHEST_OPEN_CUR MAX_OPEN_CUR
---------------- ---------------------------------------------
300              19           --查看cursor相关统计值,实例级别

SQL> select name,value from v$sysstat where name like '%cursor%';
NAME                                VALUE
----------------------------------- ----------
opened cursors cumulative            819271677
opened cursors current                     350
pinned cursors current                       6
session cursor cache hits            340959054
session cursor cache count           399411460
cursor authentications                   56465

–提供缓存值

alter system set session_cached_cursors=300 scope=spfile;

修改后要重启数据库方能生效。
–通过下面语句找出执行比差的

 SELECT  st.sql_id,
         st.executions_total,
         st.parse_calls_total,
         ROUND (100 * (1 - (st.parse_calls_total / st.executions_total)), 2)||'%' AS execute_to_parse,
         st.executions_delta,
         st.parse_calls_delta,
         ROUND (100 * (1 - (st.parse_calls_delta / st.executions_delta)), 2)||'%' AS  delta_ratio
    FROM DBA_HIST_SQLSTAT st, DBA_HIST_SQLTEXT sq, DBA_HIST_SNAPSHOT s
   WHERE     s.snap_id = st.snap_id
         AND s.begin_interval_time >=
                TO_DATE ('2017-01-01 09:30:00', 'YYYY-MM-DD HH24:MI:SS')
         AND s.end_interval_time <=
                TO_DATE ('2017-03-22 17:00:00', 'YYYY-MM-DD HH24:MI:SS')
         AND st.sql_id = sq.sql_id
         AND st.parsing_schema_name in ('小凡仙')
         AND st.executions_total != 0
         AND st.executions_delta != 0
 ORDER BY delta_ratio ;

V$SQLAREA 字段解释:
PARSE_CALLS 解析的次数
LOADS 加载的次数,即硬解析的次数
EXECUTIONS 执行的次数

下面是软软解析

begin
for i in 1 .. 10
loop 
    execute immediate 'select * from A1 t where ourcode=:sb' using i;
end loop;
end;

select sql_id, a.LOADS,a.EXECUTIONS, a.PARSE_CALLS, a.SQL_TEXT
  from v$sqlarea a
 where sql_id = 'ah4tdp2ksucnu';
SQL_ID               LOADS  EXECUTIONS  PARSE_CALLS SQL_TEXT
ah4tdp2ksucnu       1         20        2   select * from A1 t where ourcode=:sb

可以看到 解析次数明显小于执行次数

软件解析:
SQL> var oid number;
SQL> exec :oid:=20;
SQL> select * from test where object_id=:oid;
未选定行
SQL> exec :oid:=30;
SQL> select * from test where object_id=:oid;
未选定行
SQL> exec :oid:=40;
SQL> select * from test where object_id=:oid;
未选定行
SQL> exec :oid:=50;
SQL> select * from test where object_id=:oid;
未选定行
SQL_TEXT                           PARSE_CALLS LOADS EXECUTIONS
select * from test where object_id=:oid 4      1       4

这里有个疑问 就是我们在SQLPLUS里面多次执行同一条SQL语句,为什么不是软软解析呢?哪怕执行了20次,也是软解析,同样执行了20次。那么这样也无法提高该指标啊!!
因此软软解析的条件要求比较高.前面两个条件只是基础条件.
这个疑问我们下次再聊.
请关注公众号:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/ZengMuAnSha/article/details/60773621