Oracle内存结构(二)----Shared Pool的详细信息

2/9/2008来源:Oracle教程人气:6886


  
The Shared Pool

 

The Shared Pool在SGA中是SIZE较大的一个部分.有很多DBA没搞明白Shared Pool是用来做什么的,不知道怎么定Shared Pool的合适的SIZE.就随意的将它的SIZE搞得很大.有时候可能是不够大,但更多的时候是在浪费内存空间.而且太大的Shared Pool会影响性能.

 

块(Chunk):

要更好的理解Shared Pool,得对X$KSMSP表多做点研究.这个表中的每一个记录行对应的是Shared Pool内存中的一个Chunk(块).

SQL> select ksmchcom, ksmchcls, ksmchsiz from x$ksmsp;

KSMCHCOM           KSMCHCLS KSMCHSIZ

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

KGL handles       recr 496

PL/SQL MPCODE     recr 1624

dictionary cach   freeabl 4256

free memory        free 1088

library cache      freeabl 568

library cache      recr 584

multiblock rea     freeabl 2072

permanent memor    perm 1677104

row cache lru      recr 48

session param v    freeabl 2936

sql area            freeabl 2104

sql area            recr 1208

上面的是对这个表查询的例子.(对整个表的查询有5726行左右的记录)

在每个Shared Pool Chunk已经分配好之后,代码将语句转化并使其有作用的这一过程实际上就称为一次分配.这个语句可以在X$KSMSP表中的KSMCHCOM字段看到,描述分配的内存Chunk.每一个Chunk都要比它所包含的对象要大,因为每个Chunk都有多出来16字节的头信息用于存储确认Chunk的类型、类别和大小SIZE还有用于Shared Pool治理的链接指针.

内存Chunk主要有四种类别,可以从X$KSMSP表中的KSMCHCLS看到.

   free:这种类别的Chunk称为Free Chunk.它不包含有效的对象,可以自由分配.

   recr:可重建的Chunk(Recreatable Chunk),这种Chunk有包含对象,而这些对象在必要的时候是可以临时被删除并在有需要的情况下进行重建.一个例子,包含Shared SQL Statements的Chunks就可以重建

freeabl:可释放的Chunk(Freeabl Chunk),这种Chunk也有包含对象,这些对象在一个session的生存期间是经常可用的而在session断掉之后就不可用.这些Chunks可以在session断开之前进行释放,可以部分释放也可以全部释放.Freeable Chunk是不能临时被删除的,因为它是不可重建的.

perm:永久性的内存Chunk(Permanent memory Chunk).它包含的对象是不能被释放的,是永久存在于内存中的.一些大的永久性的内存Chunk内部也包含一定量的free space.可以根据需要释放给shared poo.

select

  ksmchcom  contents,

  count(*)  chunks,

  sum(decode(ksmchcls, 'recr', ksmchsiz))  recreatable,

  sum(decode(ksmchcls, 'freeabl', ksmchsiz))  freeable,

  sum(ksmchsiz)  total

from

  sys.x_$ksmsp

where

  inst_id = userenv('Instance') and

  ksmchcls not like 'R%'

group by

  ksmchcom

 

KSMCHCOM  
         CHUNKS      RECR        FREEABL     TOTAL

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

KGFF heap          6           1296         2528        3824

KGK contexts      2                       2400         2400

KGK heap          2          1136                     1136

KGL handles       571        178616                178616

KQLS heap         404        87952      524888      612840

PL/SQL DIANA      274        42168      459504      501672

PL/SQL MPCODE     57         14560      88384       102944

PLS cca hp desc   1                    168         168

PLS non-lib hp    1          2104                   2104

character set m   5                    23504       23504

dictionary cach   108                  223872      223872

fixed allocatio   9          360                   360

free memory       185                              614088

kzull             1             
       48          48

library cache     1612       268312     356312      624624

multiblock rea    1                     2072        2072

permanent memor   1                                1677104

reserved stoppe   2                                48

row cache lru     24         1168                  1168

session param v   8                    23488       23488

sql area          983        231080     1303792     1534872

table columns     19         18520                 18520

table definiti    2          176                   176

上面这个查询可以得到Shared Pool中所有Chunks的类型,类别和大小SIZE等相关信息.上面只列出部分记录.

 

Free Lists:

Shared Pool中的Free Chunks,于它们的大小为基准被组织成Free Lists或者BUCkets.下面的一张表是Bucket号和Free Chunks SIZE的对应关系:

 

 



用下面这个脚本可以查询到Chunks数和每个Free List中的Free Space量.

select

  decode(sign(ksmchsiz - 80), -1, 0, trunc(1/log(ksmchsiz - 15, 2)) - 5)

    bucket,

  sum(ksmchsiz)  free_space,

  count(*)  free_chunks,

  trunc(avg(ksmchsiz))  average_size,

  max(ksmchsiz)  biggest

from

  sys.x_$ksmsp

where

  inst_id = userenv('Instance') and

  ksmchcls = 'free'

group by

  decode(sign(ksmchsiz - 80), -1, 0, trunc(1/log(ksmchsiz - 15, 2)) - 5)

BUCKET      FREE_SPACE FREE_CHUNKS AVERAGE_SIZE BIGGEST

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

0  
         166344    3872         42           72

1           32208      374          86            96

4           928        1            928           928

6           11784      4            2946          3328

 

当一个进程需要Shared Pool Memory分配一个Chunk时,它首先会到Free Lists中查找与它所需的SIZE匹配的Chunk.假如跟所需的SIZE没有确切相符的Chunk可以分配则会继续在Free Lists中寻找一个更大的Chunk.假如找到的Chunk有24字节或者更多,则这个Chunk将会被分裂开来使用,将其余的Free Space部分返还到Free Lists中.假如上面的都找不到,则会从一个非空的Free Lists中找一个最小的Chunk给它使用.最后的方式是以LRU机制对Free Lists进行描述

   Free Lists的描述,治理和Chunk的分配都是在Shared Pool Lathes的保护下进行的.假如Shared Pool包含有大量的小的Free Chunks时,在对这个非凡的Free Lists进行描述时,Shared Pool Lathes将会被占有比较长的一段时间.实际上,经常出现的Shared Pool Lathes的竞争和等待就是因为包含了大量的小的Free Chunks.所以增加Shared Pool 的SIZE并不能减少Shared Pool Lathes Contention而且还会使得这个竞争更加严重,这就是前面提到的将Shared Pool的SIZE设为很大并不一定会提高性能原因.

  

LRU Lists:

假如一个进程没办法从Shared Pool Free Lists中获得一个Free Chunk,则它会从Shared Pool中删除一个包含有Recreatable对象的Chunks来释放足够大的Chunks满足自己的使用.

  Recreatable Chunks有两个种类:一种是Pinned在内存中的,一种是没有Pinned在内存中的.Pinned在内存中的Chunks跟用DBMS_SHARED_POOL.KEEP执行来KEEP在Shared Pool中是不一样的. DBMS_SHARED_POOL.KEEP是需要DBA来干预执行的,并且只能放在library cache中.而Pinned Chunks是自动执行的,它在Chunks包含正在使用的对象时就会自动被pinned在Shared Pool中.Pinned Recreatable Chunks是不能释放空间的,而Unpinned Recreatable Chunks是可以释放出来的.

  在Shared Pool中,Unpinned Chunks被组织成两个Lists,它们都是以LRU机制来排列的.分别被称为临时的LRU List和循环的LRU List. Chunks在Unpinned时放在MRU(most recently used)的最后面,在它们pinned时将会从这个MRU List中删除.

  在一个进程需要新分配一部分Shared Pool Memory内存时,Chunks也会从LRU List的最尾端删除.Chunks以每8个chunks为一个集而被flushed(暂时说为清空)-----首先是从临时的LRU List,然后再从循环的LRU List. Chunks是在不考虑SIZE的大小并且以LRU机制被flushed的.有一些Chunks是不能被 flushed的,如用DBMS_SHARED_POOL.KEEP执行后的包含library cache 对象的Chunks是不能被 flushed.这些Chunks通过pinned方式来代替被删除.

  Unpinned Recreatable Chunks两种类型的LRU Lists: 临时的LRU List和循环的LRU List的长度可以从fixed表X$KGHLU查询到,还有已经flush掉的Chunks数,由于pinned和unpinned而加入到LRU List或者是从LRU List删除的Chunks数.从X$KGHLU表也可以知道LRU Lists被flushed完全但不成功(不理解)的次数和最近一次请求Chunk失败的所需要Chunk SIZE.具体看下面的语句:

column kghlurcr heading "RECURRENTCHUNKS"

column kghlutrn heading "TRANSIENTCHUNKS"

column kghlufsh heading "FLUSHEDCHUNKS"

column kghluops heading "PINS ANDRELEASES"

column kghlunfu heading "ORA-4031ERRORS"

column kghlunfs heading "LAST ERRORSIZE"

 


select

  kghlurcr,

  kghlutrn,

  kghlufsh,

  kghluops,

  kghlunfu,

  kghlunfs

from

  sys.x_$kghlu

where

  inst_id = userenv('Instance')

/

 

RECURRENT TRANSIENT   FLUSHED     PINS AND    ORA-4031     LAST ERROR

CHUNKS     CHUNKS      CHUNKS      RELEASES     ERRORS       SIZE

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

121         164         148447    4126701      0         0

 

LRU Lists的长度和flush率跟前台应用和工作流量的变化有很大的依靠关系.长或短的LRU Lists都不会造成问题,但是Chunks flush则在对内存的健康治理中占用很重要的一部分.假如TRANSIENT CHUNKS长于RECURRENT CHUNKS则说明Shared Pool SIZE太大了.而Chunks flush到其它的LRU操作的命中率假如大于1/20则说明Shared Pool SIZE太小.(上面这个是别人的经验介绍)

 

Spare free memory:

假如一个大的内存请求从Free Lists和LRU Lists Flushing都不能分配,Oracle则会用另外的一种方式来实现.

Oracle的重排序并不是用来接合空闲内存的.当一些Chunks已经被释放它们将会跟后面的Chunk接合一起使用(这里所谓的接合是前面一个free chunk跟后面一个free chunk接合在一起成为一个更大的free chunk).一般地,Oracle只会在显示的执行命令ALTER SYSTEM FLUSH SHARED POOL后才会以完全的方式来接合Shared Pool中的free space.即使当Shared Pool有足够的连续的内存段时,内存请求分配也有可能失败的.假如空闲内存被分成多个小的Chunks时,也有可能没法满足大的内存请求分配.

为了满足较大的内存请求分配,Oracle用重排序的方式来释放更多的内存给Shared Pool. 实际上,Oracle在实例启动的时候只分配大概一半的原Shared Pool SIZE给Shared Pool(即划分shared_pool_size的一半内存出来给Shared Pool.等以后需要用到更多时再用重排序的方式秋释放给它).Oracle用这种方式来确保Shared Pool不会产生太多的碎片.

Oracle的多余空闲内存(Spare free memory),在Shared Pool中被隐藏在永久性的内存Chunks(permanent memory chunks)中.这些内存不在Shared Pool的Free Lists中,所以是不能立即被分配出来使用的.在V$SGASTAT可以看到free memory statistics.在有必要的时候Spare Free Memory Chunks才会被释放给Shared Pool.在这些Spare Free Memory已经被分配完给Shared Pool之后,还有对Shared Pool Memory请求但是分配不到Free Memory,则会产生ORA-4031’unable to allocate x bytes of shared memory’错误.假如在数据库运行于高峰登录量一段时间之后,Oracle还有剩余较大部分的Spare Free Memory则说明此时所设置的shared_pool_size过大.可以通过下面的脚本查看剩余的Spare Free Memory.

select

  avg(v.value)  shared_pool_size,

  greatest(avg(s.ksmsslen) - sum(p.ksmchsiz), 0)  spare_free,

  to_char(

    100 * greatest(avg(s.ksmsslen) - sum(p.ksmchsiz), 0) / avg(v.value),

    '99999'

  ) '%' wastage

from

  sys.x_$ksmss s,

  sys.x_$ksmsp p,

  sys.v_$parameter v

where

 
s.inst_id = userenv('Instance') and

  p.inst_id = userenv('Instance') and

  p.ksmchcom = 'free memory' and

  s.ksmssnam = 'free memory' and

  v.name = 'shared_pool_size'

/

 

The Reserved List:

  一般,最大的Chunks也不会超过5000bytes的.假如有请求更大的Chunks的话,Oracle用的是另外一种方式,即下面说的Reserved Shared Pool,而不会到Shared Pool Free Lists和LRU Lists中寻找适应的或者更大SIZE的Chunks来给它使用.

Oracle为大的Chunks保留了Shared Pool的一部分.缺省保留的是shared_pool_size的5%.也可以通过参数shared_pool_reserved_size设置其大小.对应参数名,这一部分是从Shared Pool中取出的一部分SIZE.

超过5000bytes的Chunks会被放到Shared Pool的保留部分中,开始是可以通过隐含参数_SHARED_POOL_RESERVED_MIN_ALLOC来设置的,但是在实例启动之后是不能修改的.所以小的Chunks是不会被放到Shared Pool的保留部分中来的. Shared Pool的保留部分中的Free Memory不被包含在一般的Shared Pool Free Lists中,而是放在一个单独的Reserved Free List(针对Reserved Shared Pool)中.然而, Reserved Shared Pool是没有属于它自己的LRU Lists 来为Unpinned Recreatable Chunks服务的.因此,在为一般的Free Lists释放内存空间时大的Chunks是不会被flushed(清空)的.而在为Reserved Shared Pool Free Lists释放内存空间时,小的Chunks是不会被flushed(Reserved Shared Pool Free Lists只有大的Chunks).(这里的大的Chunks我们指的是超过5000bytes的Chunks)

在V$SHARED_POOL_RESERVED视图我们可以查看Reserved Shared Pool的统计信息.非凡是字段REQUEST_MISSES,它显示的是从Reserved Shared Pool Free Lists中请求大的Chunks没有即刻满足的次数.这个值应当为0.也就是说,在没有flush Unpinned Recreatable Chunks的情况下在Reserved Shared Pool应该有足够多的Free Memory来满足短时间的Freeable memory的请求.查看V$SHARED_POOL_RESERVED视图中的USED_SPACE字段来确定多大的Reserved Shared Pool SIZE才是适当的.用下面的脚本查看Reserved Shared Pool 的SIZE和HWM以及使用率.最好在关闭数据库之前做查询并结合V$RESOURCE_LIMIT查看其它资源的使用.

PRompt Reserved Pool High-Water-Mark Since Instance Startup

prompt ====================================================

 

select

  sum(r.ksmchsiz) - &stopper  reserved_size,

  sum(

    r.ksmchsiz -

    decode(h.kghlunfu, 0, decode(r.indx, 1, r.ksmchsiz, 0), 0)

  ) - &stopper  high_water_mark,

  to_char(

    100 * (sum(

       r.ksmchsiz -

       decode(h.kghlunfu, 0, decode(r.indx, 1, r.ksmchsiz, 0), 0)

     ) - 48

    ) / (sum(r.ksmchsiz) - &stopper),

    '99999'

  ) '%'  "  USAGE"

from

  sys.x_$kghlu  h,

  sys.x_$ksmspr  r

where

  h.inst_id = userenv('Instance') and

  r.inst_id = userenv('Instance')

/

prompt

 

RESERVED_SIZE HIGH_WATER_MARK USAGE

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

256000        15080           6%

 


Marking Objects for Keeping:

在一个SIZE适当的SHARED POOL, dead Chunks是不会被清除出去的.任何一个清除的动作都意味着一些有用的对象也会被清除掉的危险.非凡是可重建的对象,因为它们只在一定时间内才被使用,但是在它们被重建时的代价可能会很大,因为它们可能是一个很大的对象或者需要更复杂的处理进程.

  减轻这个危险性的办法就是用DBMS_SHARED_POOL.KEEP将一些用户自己认为有用的对象KEEP到SHARED POOL中.这个程序将对象以及有关的下级对象放在library cache中并将它们标志为是KEEP在SHARED POOL中的.所以最合适做这个的时间是在实例启动之后做KEEP操作以免SHARED POOL产生更多的碎片.经常会错误的认为如包等大的对象不需要KEEP到SHARED POOL中,因为它们一般会被放到SHARED POOL的RESERVED部分,因此被清除的机率非常小.其实不然,很多大的对象实际上是以多个小的Chunks来分配SHARED POOL内存的,是没办法以它们的大小来获得非凡的保护.通过查看表X$KSMLRU可以确认哪些library cache对象需要KEEP到SHARED POOL中.

 

Flushing the Shared Pool:

接合连续的free chunks的唯一方法就是用命令ALTER SYSTEM FLUSH SHARED POOL显示的清空SHARED POOL.在实践中,清空SHARED POOL会减少shared pool latch竞争,也会降低产生ORA-4031错误机率,也会减少即刻的性能竞争,非凡是那些被KEEP到SHARED POOL中的主要对象.反之,假如那些主对象都已经KEEP到SHARED POOL中了,而且你的SHARED POOL SIZE比较适当,就要少使用FLUSH SHARED POOL,否则,实例将会在正常的运行时间对系统性能的要求非凡大.

一般地,会在晚上备份完之后来做FLUSH SHARED POOL.其它的时间就是在SHARED POOL有很多的碎片或者空间不足.在做FLUSH SHARED POOL时要确认不要对CAHED SEQUENCE产生不必要的缺口.要达到这个目的可以将SEQUENCE KEEP到SHARED POOL中,假如是单个实例也可以临时的执行ALTER SEQUENCE NOCACHE.可用下面的PROCEDURE来确认哪些SEQUENCE需要KEEP到SHRAED POOL中.假如需要做SHUTDOWN ABORT来关闭数据库时最好先将SEQUENCE KEEP到SHARED POOL中.

declare

  cursor cached_sequences is

    select

      sequence_owner,

      sequence_name

    from

      sys.dba_sequences

    where

      cache_size > 0;

  sequence_owner varchar2(30);

  sequence_name varchar2(30);

begin

  open cached_sequences;

  loop

    fetch cached_sequences into sequence_owner, sequence_name;

    exit when cached_sequences%notfound;  

    sys.dbms_shared_pool.keep(sequence_owner '.' sequence_name, 'Q');

  end loop;

end;  

/

Heaps and Subheaps

针对SHARED POOL的FIXED TABLES X$是以KSM和KGH开头,它们分别代表的意思是Oracle内存治理器和堆治理器模型.两者是紧密联系在一起工作的.内存治理器是负责与操作系统OS进行交互以获得内存供Oracle使用和内存分配信息.堆治理器则是进行动态的内存分配和治理.SHARED POOL也叫SGA HEAP就是这个原因了.

一个堆包含堆描述器和一个或多个内存区,当然也包含子堆.在这种情况下,子堆的堆描述器和内存区是以父堆的Chunks来分配的.HEAP的大小是以HEAP的类型和HEAP’S FREE LIST和LRU LIST的LIST头信息的不同而不同的.每个内存区都有头部信息指向前面的和后面的一个堆,还有此HEAP还有多少空间可分配的信息.

除了Reserved部分,SHARED POOL的子堆实际上跟SHARED POOL本身有相同的结构.内存是以Chunks分配的,Free Chunks被组织成Free Lists. Unpinned Recreatable Chunks也是被组织成两个LRU LISTS: Transient chunks和Recurrent chunks.子堆也有永久性的内存Chunks.也可以再包含子堆,深度为4.

CACHED在SHARED POOL的大部分对象实际上都是在子堆里面为一个新的Chunks查找空间,就好象是SHARED POOL自己为一个新的Chunks查找空间.所不同的是,子堆是以区来增长的,而SHARED POOL是一个固定大小的SIZE.子堆新区是以最小SIZE的区(适合对象的Chunks的最小SIZE)来分配的,所以它一般会找最小的Chunks.

 


The Large Pool:

  假如参数LARGE_POOL_SIZE有设置,则Large Pool将会在SGA中的变量区域(variable area)被配置为一个单独的堆.它不是SHARED POOL的一部分,是以large memory latch来保护的. Large Pool只包含空闲的Chunks和可释放的Chunks.不包含Recreatable Chunks,所以堆治理器的LRU机制是不被使用的.

  为了阻止在Large Pool中产生碎片,所有的Large Pool Chunks的SIZE一般都以参数_LARGE_POOL_MIN_ALLOC做为其SIZE,其缺省值为16K.这个参数不必要做调优.

  在下列情况下建议你设置LARGE_POOL_SIZE参数使用Large Pool:

       使用了MTS

       使用了Recovery Manager(RMAN)

       使用了并行的查询选项Parallel Query Option(PQO)