1、共享次(shared pool)
共享池是组成SGA的重要结构中的一个。可以通过以下命令确认SGA结构。
SQL> show sga;
Total System Global Area 9620525056 bytes
Fixed Size 2215704 bytes --固定区域
Variable Size 8522825960 bytes --可变区域
Database Buffers 1073741824 bytes
Redo Buffers 21741568 bytes
除ShowSga外,我们还可以用V$sgastat视图显示SGA各内存结构的大小
- SQL> select * from v$sgastat where rownum < 11;
- POOL NAME BYTES
- ------------ ------------------------------ ----------
- fixed_sga 1339824
- buffer_cache 293601280
- log_buffer 5132288
- shared pool dpslut_kfdsg 256
- shared pool hot latch diagnostics 80
- shared pool kkj jobq wor 4104
- shared pool vips_package_file 924
- shared pool ENQUEUE STATS 16296
- shared pool sskgplib 1052
- shared pool transaction 426316
- 已选择10行。
在一个很高的层次上来看,shared pool可以分为库缓存(library cache)和数据字典缓存(dictionary cache)。Library cache存放了最近执行的SQL语句、存储过程、函数、解析树以及执行计划等。dictionary cache则存放了在执行SQL语句过程中,所参照的数据字典的信息,包括SQL语句所涉及的表名、表的列、权限信息等。dictionary cache也叫做row cache,因为这里面的信息都是以数据行的形式存放的,而不是以数据块的形式存放的。共享池的内存区域组成,可以通过如下命令查看:
SQL> select count(*) from v$sgastat where pool = 'shared pool';
COUNT(*)
----------
897 --总共分为897个内存区域
共享池的结构大体上如下分类:
Permanent Area:process,session,segment array(enqueue,transaction ...)等。进程列表、会话列表、Enqueue列表、事务列表等的资源分配到共享池的永久区域(permanent area),与实例的寿命相同。
Library Cache:管理执行sql语句必要的全部对象(sql、表、视图、procedure等)信息。
Dictionary Cache:也叫做row cache,管理oracle所使用的字典(dictionary)信息。
Reserved Area:保留区域。为动态分配内存,oracle将分配部分保留区域。
oracle对SQL语句进行了概括和抽象,将SQL语句提炼为两部分,一部分是SQL语句的静态部分,也就是SQL语句本身的关键词、所涉及的表名称以及表的列等。另一部分就是SQL语句的动态部分,也就是SQL语句中的值(即表里的数据)。
从上面这个图中可以看到,当SQL语句进入library cache时,oracle会到dictionary cache中去找与sharedpool_test表有关的数据字典信息,比如表名、表的列等,以及用户权限等信息。如果发现dictionary cache中没有这些信息,则会将system表空间里的数据字典信息调入buffer cache内存,读取内存数据块里的数据字典内容,然后将这些读取出来的数据字典内容按照行的形式放入dictionary cache里,从而构造出dc_tables之类的对象。然后,再从dictionary cache中的行数据中取出有关的列信息放入library cache中。
共享池是利用堆这种内存管理方法进行管理。从一个物理的层面来看,shared pool是由许多内存块组成,这些内存块通常称为chunk.Chunk是shared pool中内存分配的最小单位,一个chunk中的所有内存都是连续的。这些chunk可以分为四类,这四类可以从x$ksmsp(该视图中的每个行都表示shared pool里的一个chunk)的ksmchcls字段看到:
1) free:(即马上使用)这种类型的chunk不包含有效的对象,可以不受限制的被分配。
2) recr:意味着recreatable(可再性),这种类型的chunks里包含的对象可以在需要的时候被临时移走,并且在需要的时候重新创建。比如对于很多有关共享SQL语句的chunks就是recreatable的。
3) freeabl:这种类型的chunks包含的对象都是曾经被session使用过的,并且随后会被完全或部分释放的。这种类型的chunks不能临时从内存移走,因为它们是在处理过程中间产生的,如果移走的话就无法被重建。
4) perm:意味着permanent(永久、不可再生),这种类型的chunks包含永久的对象,大型的permanent类型的chunks也可能含有可用空间,这部分可用空间可以在需要的时候释放回shared pool里。
在shared pool里,可用的chunk(free类型)会被串起来成为可用链表(free lists)或者也可以叫做buckets(一个可用链表也就是一个bucket)。我们可以使用下面的命令将shared pool的内容转储出来看看这些bucket.
- SQL> alter session set events 'immediate trace name heapdump level 2';
- 会话已更改。
- select d.value || '/' || lower(rtrim(i.instance, chr(0))) || '_ora_' ||
- 2 p.spid || '.trc' trace_file_name
- 3 from (select p.spid
- 4 from sys.v$mystat m, sys.v$session s, sys.v$process p
- 5 where m.statistic# = 1
- 6 and s.sid = m.sid
- 7 and p.addr = s.paddr) p,
- 8 (select t.instance
- 9 from sys.v$thread t, sys.v$parameter v
- 10 where v.name = 'thread'
- 11 and (v.value = 0 or t.thread# = to_number(v.value))) i,
- 12 (select value from sys.v$parameter where name = 'user_dump_dest') d;
- TRACE_FILE_NAME
- ---------------------------------------------------------------------------------------------------
- /u01/app/diag/rdbms/orcl/orcl/trace/orcl_ora_3844.trc
然后打开产生的转储文件,找到“FREE LISTS”部分:
- FREE LISTS:
- Bucket 0 size=16
- Bucket 1 size=20
- Bucket 2 size=24
- Chunk 51ffffe8 sz= 24 free " "
- Bucket 3 size=28
- Bucket 4 size=32
- Bucket 5 size=36
- Bucket 6 size=40
- Bucket 7 size=44
- Bucket 8 size=48
- Bucket 9 size=52
- Bucket 10 size=56
- Bucket 11 size=60
- Bucket 12 size=64
- Bucket 13 size=68
- Bucket 14 size=72
- Bucket 15 size=76
- Bucket 16 size=80
- Bucket 17 size=84
- Bucket 18 size=88
- Bucket 19 size=92
- Chunk 523fffa4 sz= 92 free " "
- Bucket 20 size=96
- Bucket 21 size=100
- ... ...
欲从共享池的堆分配内存的所有进程,必须获得shared pool锁存器。发生Hard Parsing时,进程从共享池分配到新sql语句所需的存储空间,此时必须获得shared pool锁存器。shared pool锁存器基本上全实例上只有一个,且需要在分配必要内存(chunk)的全过程一直拥有。因此,多个进程同时使用共享池内存时,在获取锁存器的过程中会发生争用。获取shared pool锁存器过程中发生争用,则等待latch: shared pool事件。
oracle 9i以上版本开始,共享次可分为多个副池(最多7个)进行管理。利用_KGHDSIDX_COUNT隐含参数,可管理副池的数量。因此,共享池较大时分为多个副池管理,所以能减少shared pool锁存器争用。通过如下语句可以看见数据库中的shared pool数量:
- SQL> select count(*) from v$latch_children where name = 'shared pool';
- COUNT(*)
- ----------
- 7
在shared pool上面的等待事件就是:
- SQL> select name,parameter1,parameter2,parameter3 from v$event_name where name like 'latch: shared pool';
- NAME PARAMETER1 PARAMETER2 PARAMETER3
- ------------------------------ -------------------- -------------------- --------------------
- latch: shared pool address number tries
2.库高速缓冲区(library cache)结构
共享池中最关键的部分是库高速缓冲区,它是管理与sql语句的执行相关的所有信息的区域。库高速缓冲区是Hash Table -> Bucket -> Chain -> Handle -> Object的结构。可以把library cache理解为一本书,而SQL语句的对象就是书中的页,而句柄就是目录,通过目录可以快速定位到指定内容的页。
oracle 利用应用于对象名称上的Hash函数生成的Hash值,分配适当的Hash Bucket,拥有相同Hash值的对象通过Chain(列)管理。一个Library Cache Handle(以下简称Handle)管理一个Library Cache Object(以下简称LCO)。Handle对实际LCO起到meta信息和指针作用,LCO保存着实际信息。LCO所包含的信息中重要信息如下:
Dependency Table:当前LCO依赖其它LCO的信息。
Child Table:关于当前LCO的子LCO的信息。
Data Blocks:LCO包含的实际信息(数据)所存储的chunk区域的指针信息。
欲检索库高速缓冲区的所有进程,就必须获得保护相应Hash Bucket的Library cache锁存器。接近库高速缓冲区的过程中发生Library cache锁存器争用时,进程等待latch: library cache事件。
如果library cache锁存器用于库高速缓冲区检索的同步,library cache lock和library cache pin这两个TX锁就会起到保护Handle和LCO的所用。如利用alter table ... 命令修改表的进程,对存储表信息的LCO,必须以Exclusive模式获得library cache lock。如果在获得library cache lock和library cache pin过程中发生争用,就会各自等待library cache lock事件和library cache pin事件。
3、sql的执行
用户请求执行sql,oracle则会利用上面说明的内存和锁存器执行必要的工作。将这些内容按时间整理结果如下:
(1)用户请求执行新sql时,oracle在执行基本语法和权限检查等步骤后,获得Hash Bucket的library cache锁存器,然后确认库高速缓冲区上是否存在相同的sql,即相同的LCO。若在获得library cache锁存器过程中发生争用,则等待latch: library cache事件。存在相同LCO存在时直接跳至第(8)阶段执行,此过程称之为soft parsing。每当发生sql parsing请求时,oracle都会增加parse count(total)统计值。
(2)若不存在相同sql,在获得 library cache锁存器后,从空闲列上查找最合适大小的空闲chunk。如果在获得shared pool锁存器过程中发生争用,则等待latch: shared pool事件。oracle会一直拥有shared pool锁存器,直到确保chunk为止。
(3)若不存在最适合大小的空闲chunk,则查找更大的空闲chunk后分割(split)使用,分割后剩下的内存区域重新登记到适当的空闲了。
(4)若检索了所有空闲列也没有找到恰当的空闲chunk,则检索LRU列。LRU列上的chunk是重建的,而且是当前不使用的(还没有pin的)。
(5)若在LRU列上检索也不能确保适当大小的chunk,则追加分配共享池内的剩余内存空间。
(6)如果以上过程失败,则发生错误ORA-4031。
(7)若找到合适的chunk,对sql相应的handle以Exclusive模式获得library cache lock,并创建LCO信息。创建LCO后,library cache lock变换为NULL模式,将library cache pin以Exclusive获得后,创建执行计划(execution plan)。第(2)~(7)号过程称为Hard parsing。如若发生Hard parsing,oracle将增加parse count(hard)统计值。若在Hard parsing过程中发现sql语句的错误,则parse count(hard)统计值和parse count(failure)统计值将会一起增加。
(8)oracle对sql cursor以shared模式获得library cache lock和library cache pin,并执行sql。这个阶段称为执行阶段。sql cursor所参考的LCO,基本上与sql cursor相同的模式获得library cache lock和library cache pin。但是执行修改对象信息(如DDL语句)工作时,对相应的对象所对应的LCO以Exclusive模式获得library cache lock和library cache pin。
(9)oracle对执行结束的sql cursor fetch数据。这个过程就是fetch阶段。在fetch阶段里,sql cursor将library cache lock变化为null模式,并解除library cache pin。
parsing 阶段的cpu使用时间和执行parsing时经历时间记录在parse time cpu统计值和parse time elapsed统计值上。若在执行parsing过程中,为获得锁存器或TX锁等待时间拖长时,parse time elapsed统计值表现的比parse time cpu统计值高出许多。特别是多个会话同时执行hard parsing时,会出现parse time elapsed统计值大幅增加的现象,这是因为在获得大部分library cache锁存器或shared pool锁存器过程中发生争用,导致等待时间相应增加。
请注意:即便存在许多空闲chunk,但是只要未发现能承载最适合大小的理想空闲 chunk,oracle就不再执行工作。共享池的空闲内存(free memory)虽然很富裕,但还是会出现错误ORA-4031就是这个原因。这种现象大部分发生在过度hard parsing导致空闲chunk分割的时候,这种共享碎片化也影响shared pool锁存器争用。共享碎片化,则空闲列包含许多空闲chunk。为了分配空闲chunk检索空闲列的过程中,需要获得shared pool锁存器,所以chunk被分割得太碎,空闲列检索时间会增加,相应地拥有shared pool锁存器的时间也会增加。
发生错误ORA- 4031时,alter system flush shared_pool 命令作为应急措施使用,在共享池flush时,连续的空闲chunk会聚合,所以下次请求sql是发现相当大小的空闲chunk可能性提高。但是 shared pool的flush不是错误ORA-4031的解决方案,要知道flush也可能会引起其它性能问题。
每次发生parsing 时,均需要获得library cache锁存器,所以发生争用的概率特别高。library cache锁存器争用与系统性能有直接联系,所以oracle为了解决library cache锁存器争用提供了多种方法。第一,对于PL/SQL内反复执行的sql cursor时,只有在最初执行时parsing,之后没有soft parsing也可以执行。因为不发生soft parsing,所以也不获得library cache锁存器。oracle将sql cursor相应的LCO pin在SGA上,所以即便没有获得library cache锁存器也可以参考LCO。第二,提供在会话内部里缓存LCO位置的功能,这就是session cursor caching。oracle将一个会话内执行三次以上的sql cursor的位置和sql文本,存在到SGA,所存储的sql cursor数量由SESSION_CACHED_CURSORS参数决定。即便使用了次功能,也依然会发生soft parsing,需要获得library cache锁存器,但不用检索库高速缓存区结构,而直接找到LCO位置,所以能缩短获得library cache锁存器的时间,相应的library cache锁存器所引起的争用也会减少。