1.1.4.2.字典缓存(Dictionary Cache)
数据字典是有关于数据库的参考信息、数据库的结 构信息和数据库中的用户信息的一组表和视图的集合,如我们常用到的V$视图、DBA_视图都属于数据字典。在sql语句解析的过程 中,Oracle可以非常迅速的访问(如果需要的话)这些数据字典,在sql Trace中,这种对数据字典的访问就被统计为回调(recursive calls)。 看下面例子:
第一调用语句,需要做硬解析:
sql> select * from T_COMPANY;
9999 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3356521258
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%cpu)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10000 | 156K| 9 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| T_COMPANY | 10000 | 156K| 9 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
355 recursive calls
0 db block gets
764 consistent gets
39 physical reads
116 redo size
305479 bytes sent via sql*Net to client
7711 bytes received via sql*Net from client
668 sql*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
9999 rows processed
可以看到,Recursive Calls高达355。第二次调 用,无需解析,直接使用共享sql区中缓存:
sql> /
9999 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3356521258
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%cpu)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10000 | 156K| 9 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| T_COMPANY | 10000 | 156K| 9 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
705 consistent gets
0 physical reads
0 redo size
305479 bytes sent via sql*Net to client
7711 bytes received via sql*Net from client
668 sql*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
9999 rows processed
由于没做解析,这时recursive calls为0。
当然,recursive calls并不仅仅发生在解析的时候。由于数据字典记录了所有对象的结构、数据信息,因此在对象结构、数据发生变化时都会访问数据字典:
sql> delete from t_company where rownum=1;
1 row deleted.
...
Statistics
----------------------------------------------------------
360 recursive calls
...
sql> /
1 row deleted.
...
Statistics
----------------------------------------------------------
4 recursive calls
...
sql> /
...
Statistics
----------------------------------------------------------
4 recursive calls
...
可以看到,上面的delete语句在第一次执行时,包括因解析和数据改动导致对数据字典的访问,因此recursive calls较高,为360。在随后的执行中,因为没有做解析,所以recursive calls大大减少,只有4,而这4个recursive calls是因为数据改变而需要对数据字典的访问。
因为Oracle对 数据字典访问如此频繁,因此内存中有两处地方被专门用于存放数据字典。一个地方就是数据字典缓存(Data Dictionary Cache)。数据字典缓存也被称为行缓存(Row Cache), 因为它是以记录行为单元存储数据的,而不像Buffer Cache是以数据块为单元存储数据。内存中另外一个存储数据字典的地方是库缓存。所有Oracle的 用户都可以访问这两个地方以获取数据字典信息。
1.1.4.3.共享池的内存管理
通常来说,共享池是根据修正过的LRU算法来是否其中的对象(共享sql区和数据自动记 录行)的,否则这些对象就一直保持在共享池中。如果共享池需要为一个新对象分配内存,并且共享池中没有足够内存时,内存中那些不经常使用的对象就被释放 掉。一个被许多会话使用过的共享池对象,即使最初创建它的进程已经结束,只要它是有用的,都会被修正过的LRU算 法一直保持在共享池中。这样就使一个多用户的Oracle系统对sql语句的处理和内存消耗最小。
注意,即使一个共享sql区与一个打开的游标相关,但如果它长时间没有被使用,它还是可能会被从共享池中释放出来。而此时如果打开 的游标还需要运行它的相关语句,Oracle就会重新解析语句,并分配新的共享sql区。
当一条sql语 句被提交给Oracle执行,Oracle会 自动执行以下的内存分配步骤:
1.Oracle检查共享池,看是否已经存在关于这条语句的共享sql区。如果存在,这个共享sql区就被用于执行这条 语句。而如果不存在,Oracle就从共享池中分配一块新的共享sql区给这条语句。同时,无论共享sql区存在与否,Oracle都会为用户分配一块私有sql区以保存这 条语句相关信息(如变量值)。
2.Oracle为会话分配一个私有sql区。 私有sql区的所在与会话的连接方式相关。
下面是Oracle执 行一条语句时共享池内存分配过程的伪代码:
execute_sql(statement)
{
if ((shared_sql_area = find_shared_sql_area(statement)) == NULL)
{
if (!allocate_from_shared_pool(&new_buffer))
{
if (new_buffer = find_age_area_by_lru(size_of(statement)) == NULL)
{
raise(4031);
return 0;
}
}
shared_sql_area = set_shared_sql_area(new_buffer);
parse_sql(statement,shared_sql_area);
}
private_sql_area = allocate_private_sql_area(statement);
run_sql(statement,shared_sql_area,private_sql_area);
return 1;
}
在以下情况下,Oracle也会将共享sql区从共享池中释放出来:
·当使用ANALYZE语句更新或删除表、簇或索引的统计信息时,所有与被分析对象相关的共享sql区都被从共享池中释放掉。当下一次被释放掉的语句被执行时,又重新在一个新的共享sql区中根据被更新过的统计信息重新解析。
·当对象结构被修改过后,与该对象相关的所有共sql区都被标识为无效(invalid)。在下一次运行语句时再重新解析语句。
·如果数据库的全局数据库名(Global Database Name)被修改了,共享池中的 所有信息都会被清空掉。
·DBA通过手工方式清空共享池:
ALTER SYSTEM FLUSH SHARED_POOL;
Shared Pool能被分成几个区域,分别被不 同的latch(latch数最大为7,可以通过隐含参数_kghdsidx_count设 置)保护。
表x$kghlu可 以查看shared pool中的LRU列表。当满足以下条件之一时,shared pool会分为多个区,分别有不同的LRU链表管理:
·在10g之前版本,如果shared pool大于128M、cpu数 量大于4;
·Oracle数据库版本为10g
这时,在x$kghlu中 就会对应不同记录。
1.1.4.4.保留共享池
前面提到,如果Oracle解析一个PL/sql程序单元,也需要从 共享池中分配内存给这些程序单元对象。由于这些对象本一般比较大(如包),所以分配的内存空间也相对较大。系统经过长时间运行后,共享池可能存在大量内存 碎片,导致无法满足对于大块内存段的分配。
为了使有足够空间缓存大程序块,Oracle专门从共享池内置出一块区域来来分配内存保持这些大块。这个保留共享池的默认大小是共享池的5%。它的大小也可以通过参数SHARED_POOL_RESERVED_SIZE来 调整。保留区是从共享池中分配,不是直接从SGA中分配的,它是共享池的保留部分,用于存储大块 段。
Shared Pool中内存大于5000字节的大段就会被存放在共享池的保留部分。而这个大小限制是通过隐含参数_SHARED_POOL_RESERVED_MIN_ALLOC来设定的(如前面所说,隐含参数不要去修改 它)。除了在实例启动过程中,所有小于这个数的内存段永远都不会放到保留部分中,而大于这个值的大内存段也永远不会存放到非保留区中,即使共享池的空间不 够用的情况下也是如此。
保留区的空闲内存也不会被包含在普通共享池的空 闲列表中。它会维护一个单独的空闲列表。保留池也不会在它的LRU列表中存放可重建(Recreatable关于内存段的各种状态我们在后面的内容中再介绍)段。当释放普通共享池空闲列表上的内存时是不会清除这些大段的,同样,在释放保留池的空闲列表上的大内存 段时也不会清除普通共享池中内存。
通过视图V$SHARED_POOL_RESERVED可 以查到保留池的统计信息。其中字段REQUEST_MISSES记录了没有立即从空闲列表中得到可 用的大内存段请求次数。这个值要为0。因为保留区必须要有足够个空闲内存来适应那些短期的内存请 求,而无需将那些需要长期cache住的没被pin住 的可重建的段清除。否则就需要考虑增大SHARED_POOL_RESERVED_SIZE了。
你可以通过观察视图V$SHARED_POOL_RESERVED的MAX_USED_SPACE字 段来判断保留池的大小是否合适。大多数情况下,你会观察到保留池是很少被使用的,也就是说5%的保 留池空间可能有些浪费。但这需要经过长期观察来决定是否需要调整保留池大小。
保留区使用shared pool的LRU链表来管理内存块,但是 在做扫描时,相互是不受影响的。例如,内存管理器扫描shared pool的LRU链表,清出空间以分配给一个小于5000字节的内存请求,是不会清出保留区的内存块的,相反亦然。
1.1.4.5.将重要、常用对象保持(Keep)在共享池中
前面提到,根据LRU算法,一些一段时间没有使用到的内存块会被情况释放。这就可能导致一些重要的对象(如一个含有大量通用算 法函数的包、被cache的序列)被从内存中清除掉。这些对象可能只是间歇被使用,但是因为他们的 处理过程复杂(不仅包本身重新分配内存、解析,还要检查里面的所有语句),要在内存中重建他们的代价非常大。
我们可以通过调用存储过程DBMS_SHARED_POOL.KEEP将这些对象保持在共享池中来降低这种风险。这个存储过程立即将对象 及其从事对象载入library cache中,并将他们都标记为保持(Keeping)状态。对于这种对象,我 们建议在实例启动时就Keep住,以减少内存碎片的几率。
有一种观点认为那些大对象(如包)是没有必要被Keep住的,因为他们会被保持在共享池的保留区(如前所述,这个区通常使用率很低),所以一般不可能被清 出。这个观点是错误滴!因为大多数大对象实际上是被分为多个小的内存段被载入共享池的,因此根本不会因为对象的大小而受到特别的保护。
另外,也不要通过频繁调用某些对象以防止他们被 从共享池中清出。如果共享池大小设置合理,在系统运行的高峰时期,LRU链表会相对较短,那些没有 被pin住的对象会很快被清出,除非他们被keep住 了。
1.1.4.6.关于Shared Pool的重要参数
这里再介绍与共享池相关的一些重要参数。
·SHARED_POOL_SIZE
这个参数我们前面已经提到,它指定了Shared Pool的大小。在32位系统中,这个 参数的默认值是8M,而64位系统中的默认 值位64M。
但是,在SGA中 还存在一块叫内部SGA消耗(Internal SGA Overhead)的内存被放置在共享池中。在9i及之前版本,共享池 的统计大小(通过v$sgastat视图统计)为SHARED_POOL_SIZE +内部SGA消耗大小。而10g以 后,SHARED_POOL_SIZE就已经包含了这部分内存大小。因此在10g中,共享池的实际使用大小就是SHARED_POOL_SIZE -内部SGA消耗大小,这在配置共享池大小时需要考虑进去,否则,扶过SHARED_POOL_SIZE设置过小,在实例启动时就会报ORA-00371错 误。
看9i中 的结果:
sql> show parameter shared_pool_size
NAME TYPE VALUE
----------------------------- ----------- --------------
shared_pool_size big integer 41943040
sql> select sum(bytes) from v$sgastat where pool = 'shared pool';
SUM(BYTES)
----------
58720256
sql>
·SHARED_POOL_RESERVED_SIZE
这个参数前面已经提到,指定了共享池中缓存大内 存对象的保留区的大小。这里不再赘述。
·_SHARED_POOL_RESERVED_MIN_ALLOC
这个参数前面也已经介绍,设置了进入保留区的对 象大小的阀值。
原文链接:https://www.f2er.com/oracle/207936.html