Oracle内存全面分析 4
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这个参数前面也已经介绍,设置了进入保留区的对 象大小的阀值。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |