我们知道shared _pool非常重要,在自动管理的环境情,可能会被其他的内存组件占用shared_pool的空间,当shared_pool空间不够时会引发各种各样的问题。在自动管理内存的环境下也可以定义各种组件的大小,这时候的定义是表示一个组件的最小值。所以建议给shared_pool_size手动设置一个最小值,这样保证shared_pool不至于太小而引发的各种问题。初始大小设置为(memory_target*0.6)*0.4 .观察一段时间,再根据实际情况做部分修改。
共享池当前大小,在手动管理模式下查参数shared_pool_size,在自动管理时,查动态性能视图:
SQL> select COMPONENT,CURRENT_SIZE,USER_SPECIFIED_SIZE from $sga_dynamic_components;
或
SQL > select sum(bytes) from v$sgastat where pool='shared pool';
subpool
只有一个共享池,则latch争用会比较频繁,早期版本如果分配过大的共享池内存则管理成本上的负担很严重。因为搜索对象要持有latch,对象多则搜索时间长,相对的latch持有时间久就容易产生latch争用。所以引入了共享池多个子池的方式,类似于working set。
Sharepool最多可以有7个子池,即使采用多个子池也不能彻底解决latch:shared pool,根源基本都是硬解析造成的。
启用方式
1. CPU数量
1-4 CPU's = 1 subpool
5-8 CPU's = 2 subpools
9-12 CPU's = 3 subpools
2. 内存大小
ASMM和AMM环境.根据sga_target,memory_target计算.
手动管理时.根据指定的shared_pool_size决定.
9I要求约128M一个subpool,10G要求约256M一个subpool,11G要求越512M一个ubpool.
3. _kghdsidx_count 隐含参数来确定subpool数量,优先级最高。
注:无论哪种修改方式影响subpool数量,都要重启实例才生效.
subpool具体多少个,是在实例启动时根据上述方法计算得来的,不能动态改变。
SQL> select name,value,ISDEFAULT,DESCRIPTION from h$parameter where name='_kghdsidx_count';
NAME VALUE ISDEFAULT DESCRIPTION
---------------- ---------- --------- --------------------------------------------------
_kghdsidx_count 1 TRUE max kghdsidx count
share pool latch和subpool一对一关系,因此通过如下查询可以判断已经启用的subpool数量。
SQL>select name,gets from v$latch_children where name='shared pool';
通过调整(扩大)共享池大小或调整隐含参数来达到增加subpool的目的,具体如下:
SQL> alter system set SGA_target=4G;
或
SQL> alter system set memory_target=4G;
或
SQL> alter system set "_kghdsidx_count"=4 scope=spfile;
subpool缓解latch:shared pool的争用,即使采用多个子池也不能彻底解决latch:shared pool。
根源基本都是硬解析造成的。
SQL> select name,addr,latch#,gets,misses from v$latch_children where name='shared pool';
free list & LRU
内存分配给shared_pool后,被拆分成多组不同大小的chunk。这些chunk一部分被使用,一部分属于空闲的。空闲的chunk挂在free list上管理。使用的chunk挂在LRU链表上。这里的LRU和buffer_cache中的LRU过期算法决然不同分为recurrent 周期,transient 瞬时
free list结构:
head header:
就是链表头.
chunk
chunk是shared pool分配的最小单元
类似数据文件中的block
通常所需的chunk都是小于4K的,大于的会分配到保留区中去
相同大小的chunks被一个bucket所管理
chunk的类型
SQL> select distinct KSMCHCLS from x$ksmsp;
KSMCHCLS
--------
recr
freeabl
R-freea
perm
R-free
free
6 rows selected.
chunk基本四类:
free
存在于free list上的chunk.
新空间申请,优先从这里获得.随时可以被分配.
perm (permanent)
分配给系统对象使用chunk.
fixed table,fixed view等系统对象.
实例生存周期中一直会存在.不会被释放.
recr (recreatable)
可以被别的对象重建的chunk
可以在被其他对象需要时移走,并且在需要时重新创建.
例如:对象失效后,再次使用的reload操作.
通常也是一组freeable chunk的带头大哥.
freeable
如果freeable为首的recr被覆盖,则这组freeable也可以随recr一起覆盖或转为free
但recr没有被覆盖时,freeable不可以释放.
R-* 代表reserved空间中的chunk.意思和上面一致.
bucket
因为每条SQL所申请的内存大小不一(因为SQL_TEXT不同)
所以为了加速定位符合所申请大小的chunk,将chunk上层由bucket分组管理
bucket是在实例启动时oracle分配好的.
shared申请内存的流程
1.获得shared pool latch,获得不到产生等待,等待事件 latch:shared pool
2.搜索空闲空间
如果有恰好大小的chunk 直接使用
如果没有恰好大小的chunk则找比申请值大的chunk,此时会切割这个chunk
按申请大小来切割,多余的放到大小合适的bucket上,
切割后的chunk不一定回到原bucket里.
freelist上的小chunk是不能合并的.即使他们是相邻的.因为合并的统筹操作过程代价高.
这就是共享池碎片的原因.
3.如果没有空闲空间根据LRU算法开始搜索。 为了减少LRU链表中的对象,使之搜索更快
oracle把共享池的LRU分为:
1.周期LRU链表(recurrent)
类似于buffer_cache的主LRU(热)
2.瞬时LRU链表(transient)
类似于buffer_cache的辅LRU(冷)
硬解析后子游标的chunk放在LRU链表中的瞬时链表,出于可能以后不会被用到的想法。其后第一次软解析时(每个会话第一次都是软解析,这里说的是硬解析后的第一次软解析) 将用到的子游标chunk由瞬时LRU链表,移动到周期LRU, 这次移动需要shared pool latch的几次持有。
如果shared pool latch争用基本可以定位就是硬解析问题.
解决:
1.绑定变量
2.subpool
3.转为手动管理SGA,自动管理过程中shared_pool_size加大,也会持有shared pool latch
4.cursor_sharing来优化,但技术不成熟,可能会出现Bug。
SQL解析及cursor
为了将用户写的可读的SQL文本转化为oracle认识的且可执行的语句,这个过程就叫做解析过程。
解析分为硬解析和软解析。当一句SQL第一次被执行时必须进行硬解析。
当客户端发出一条SQL语句(也可以是一个存储过程或者一个匿名PL/SQL块)进入shared pool时
(注意,我们从前面已经知道,oracle对这些SQL不叫做SQL语句,而是称为游标(cursor)。因为oracle在处理SQL时,需要很多相关的辅助信息,这些辅助信息与SQL语句一起组成了游标),oracle首先将SQL文本转化为ASCII字符,然后根据hash函数计算其对应的hash值(hash_value)。根据计算出的hash值到library cache中找到对应的bucket,然后比较bucket里是否存在该SQL语句。
如果不存在,则需要按照我们前面所描述的,获得shared pool latch,然后在shared pool中的可用chunk链表(也就是bucket)上找到一个可用的chunk,然后释放shared pool latch。在获得了chunk以后,这块chunk就可以认为是进入了library cache。然后,进行硬解析过程。硬解析包括以下几个步骤:
1) 对SQL语句进行语法检查,看是否有语法错误。比如没有写from等。如果有,则退出解析过程。
2) 到数据字典里校验SQL语句涉及的对象和列是否都存在。如果不存在,则退出解析过程。
3) 将对象进行名称转换。比如将同名词翻译成实际的对象等。如果转换失败,则退出解析过程。
4) 检查游标里用户是否具有访问SQL语句里所引用的对象的权限。如果没有权限,则退出解析过程。
5) 通过优化器创建一个最优的执行计划。这一步是最消耗CPU资源的。
6) 将该游标所产生的执行计划、SQL文本等装载进library cache的若干个heap中。
在硬解析的过程中,进程会一直持有library cach latch,直到硬解析结束。硬解析结束以后,会为该SQL产生两个游标,一个是父游标,另一个是子游标。父游标里主要包含两种信息:SQL文本以及优化目标(optimizer goal)。父游标在第一次打开时被锁定,直到其他所有的session都关闭该游标后才被解锁。
一个父游标下可以有多个子游标。有父子游标目的是为了SQL_ID相同时,要共享执行计划做软解析。但即使SQL_ID匹配也不见得能够共享执行计划。执行计划在子游标里存放,不能共享的原因很多,V$SQL_SHARED_CURSOR 视图会告诉我们不能共享的原因。
如何找到library cache里的子游标中的执行计划? library cache的结构和buffer cache宏观结构很相似,对比如下:
cbc latch 对应 library cache latch
hash bucket 一致
BH 对应 library object handle
hash chain 对应 library object chain (LOC)
但library object chain (LOC)其下的内容就不同了。具体如下下图所示:
通过对shared pool的dump来可以看到具体结构:
对共享池做dump:(可选)
对内存做个2050级别转储,这会把整个SHARED POOL转储出来.要提前将shared pool 缩小.
ALTER SESSION SET EVENTS 'immediate trace name heapdump level 2050';
对library cache做dump:
ALTER SESSION SET EVENTS 'immediate trace name library_cache level 16';
通过如下方法可以看到dump后的trace文件:
SYS@ora11g> conn / as sysdba
Connected.
SYS@ora11g> ALTER SESSION SET EVENTS 'immediate trace name library_cache level 16';
Session altered.
SYS@ora11g> select * from v$diag_info where name='Default Trace File';
通过如下SQL可以确定某SQL语句的如下信息:
SYS@ora11g> select KGLHDPAR,KGLHDADR,KGLOBHD0,KGLOBHD6,KGLNAOBJ from x$kglob
where KGLHDPAR=KGLHDADR and KGLNAOBJ='某SQL文本' ;
x$kglob.KGLHDPAR=x$kglob.KGLHDADR 则是父游标句柄地址.
x$kglob.KGLHDPAR<>x$kglob.KGLHDADR 则是子游标句柄地址.
x$kglob.KGLNAOBJ 是对象名,对应一个cursor就是sql_text.
x$kglob.KGLOBHD0 是父/子游标heap0的DS(描述符)
x$kglob.KGLOBHD6 是子游标heap6的DS(描述符)
1.找到这条sql对应的父游标library object handle地址SYS@ora11g> select KGLHDPAR,KGLHDADR,KGLOBHD0,KGLOBHD6,KGLNAOBJ from x$kglob
where KGLHDPAR=KGLHDADR and KGLNAOBJ='某SQL文本' ;
2.从library cache转储中查看父游标parent cursor handle:0000000067D30048
用vim搜索方法 /\c67D30048
library cache lock
1 null 可破碎的解析锁
2 shared
3 exclusive
library cache pin
1 null 可破碎的解析锁
2 shared
3 exclusive
在library cache中,多个session之间读写阻塞关系如下:
读读不阻塞
读阻塞写
写阻塞读
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/18841027/viewspace-1651667/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/18841027/viewspace-1651667/