Oracle内存全面分析(4) 1.1.4.3. 共享池的内存管理 通常来说,共享池是根据修正过的LRU算法来 (决定)是否(覆盖或叫刷出)其中的对象 (共享SQL区和数据自动记录行)的,否则这些对象就一直保持在共享池中: 如果共享池需要为一个新对象分配内存,并且共享
Oracle内存全面分析(4)
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设置)保护。 也就是说, shared pool会分为多个区(称为 shared pool 子池?),分别由不同的LRU链表管理不同的子池,而每个 LRU链表 由一个 latch 所保护。
表x$kghlu可以查看shared pool中的LRU列表 。
当满足以下两个条件之一时,shared pool会分为多个 区(称为 shared pool 子池?) ,分别有不同的LRU链表管理:
· 1、 在10g之前版本,如果shared pool大于128M、CPU数量大于4;
· 2、 Oracle数据库版本为10g
这时(即 满足以上两个条件之一时 ),在x$kghlu中就会对应(多个)不同记录(,而不是只有一个记录)。
1.1.4.4. 保留共享池
前面提到,如果Oracle解析一个 PL/SQL程序单元,也需要从共享池中分配内存给这些程序单元对象。由于这些对象本一般比较大(如包),所以分配的内存空间也相对较大。系统经过长时间运行后, 共享池可能存在大量内存碎片,导致无法满足对于大块内存段的分配。
为了使有足够空间缓存大程序块,Oracle专门从共享池内置出一块区域来来分配内存保持这些大块。
这个保留共享池的默认大小是共享池的5%。
它的大小也可以通过参数SHARED_POOL_RESERVED_SIZE来调整。
保留区是从共享池中分配 ,不是直接从SGA中分配的, 它是共享池的保留部分,用于存储大块段。
Shared Pool中 内存大于5000字节的大段 (chunk)就会被存放在共享池的保留部分。而这个大小限制是通过隐含参数_SHARED_POOL_RESERVED_MIN_ALLOC来设定的(如前面所说,隐含参数不要去修改它)。 除了在实例启动过程中,所有小于这个数的内存段永远都不会放到保留部分中,而大于这个值的大内存段也永远不会存放到非保留区中, 即使共享池的空间不够用的情况下也是如此。
保留区的空闲内存也不会被包含在普通共享池的空闲列表中。
它会维护一个 单独的空闲列表。
保留池也 不会在它的LRU列表 中存放 可重建 (Recreatable 关于内存段的各种状态我们在后面的内容中再介绍)段(chunk)。
当 释放 普通共享池空闲列表上的内存时是 不会清除 这些大段的,同样,在释放保留池的空闲列表上的大内存段时也不会清除普通共享池中内存。
通过视图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的序列)被从内存中清除掉。这些对象可能只是间歇被使用,但是因为他们的处理过程复杂( 不仅包本身重新分配内存、解析,还要检查里面的所有语句--即 重建 过程为如此。如,游标可以重建,重新解析SQL语句为游标就是所谓重建 ),要在内存中重建他们的代价非常大。
我们可以通过调用存储过程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这个参数前面也已经介绍,设置了进入保留区的对象大小的阀值。
1.1.4.7. 共享池的重要视图最后,我们再介绍关于共享池的一些重要视图
· v$shared_pool_advice这个视图与Oracle的另外一个优化建议器——共享池建议器 ——相关。我们可以根据这个视图里面oracle所做的预测数据来调整共享池大小。 它的预测范围是从当前值的10%到200%之间。 视图的结构如下
字段
数据类型
描述
SHARED_POOL_SIZE_FOR_ESTIMATE
NUMBER
估算的共享池大小(M为单位)
SHARED_POOL_SIZE_FACTOR
NUMBER
估算的共享池大小与当前大小比
ESTD_LC_SIZE
NUMBER
估算共享池中用于库缓存的大小(M为单位)
ESTD_LC_MEMORY_OBJECTS
NUMBER
估算共享池中库缓存的内存对象数
ESTD_LC_TIME_SAVED
NUMBER
估算将可以节省的解析时间。 这些节省的时间来自于请求处理一个对象时,重新将它载入共享池的时间消耗和直接从库缓存中读取的时间消耗的差值。
ESTD_LC_TIME_SAVED_FACTOR
NUMBER
估算的节省的解析时间与当前节省解析时间的比。
ESTD_LC_MEMORY_OBJECT_HITS
NUMBER
估算的可以直接从共享池中命中库缓存的内存对象的命中次数。
关于如何根据建议器采用合理的共享池大小的方法,和前面提到的缓冲区建议器的使用方法类似,不再赘述。
· V$SHARED_POOL_RESERVED前面提到了这个视图。这个视图存放了共享池保留区的统计信息。可以根据这些信息来调整保留区。视图结构如下:
Column
Datatype
Description
以下字段只有当参数SHARED_POOL_RESERVED_SIZE设置了才有效。
FREE_SPACE
NUMBER
保留区的(当期)空闲空间数。
AVG_FREE_SIZE
NUMBER
保留区的(自实例启动以来历次)空闲空间平均数。
FREE_COUNT
NUMBER
保留区的空闲内存块数
MAX_FREE_SIZE
NUMBER
最大的保留区空闲空间数。
USED_SPACE
NUMBER
保留区使用空间数。
AVG_USED_SIZE
NUMBER
保留区 (自实例启动以来历次) 使用空间平均数。
USED_COUNT
NUMBER
保留区使用内存块数。
MAX_USED_SIZE
NUMBER
最大保留区使用空间数
REQUESTS
NUMBER
请求再保留区查找空闲内存块的次数。
REQUEST_MISSES
NUMBER
无法满足查找保留区空闲内存块请求,需要从LRU列表中清出对象的次数。
LAST_MISS_SIZE
NUMBER
请求的内存大小,这次请求是最后一次需要从LRU列表清出对象来满足的请求。
MAX_MISS_SIZE
NUMBER
所有需要从LRU列表清出对象来满足的请求中的内存最大大小
以下字段无论参数SHARED_POOL_RESERVED_SIZE是否设置了都有效。
REQUEST_FAILURES
NUMBER
没有内存能满足的请求次数(导致4031错误的请求)
LAST_FAILURE_SIZE
NUMBER
没有内存能满足的请求所需的内存大小(导致4031错误的请求)
ABORTED_REQUEST_THRESHOLD
NUMBER
不清出对象的情况下,导致4031错误的最小请求大小。
ABORTED_REQUESTS
NUMBER
不清出对象的情况下,导致4031错误的请求次数。。
LAST_ABORTED_SIZE
NUMBER
不清出对象的情况下,最后一次导致4031错误的请求大小。
我们可以根据后面4个字段值来决定如何设置保留区的大小以避免4031错误的发生。
· v$db_object_cache这一视图显示了所有被缓存在library cache中的对象,包括表、索引、簇、同义词、PL/SQL存储过程和包以及触发器(的定义)。
字段
数据类型
说明
OWNER
VARCHAR2(64)
对象所有者
NAME
VARCHAR2(1000)
对象名称
DB_LINK
VARCHAR2(64)
如果对象存在db link的话,db link的名称
NAMESPACE
VARCHAR2(28)
库缓存的对象命名空间,包括: TABLE/PROCEDURE, BODY, TRIGGER, INDEX, CLUSTER, OBJECT, CURSOR, INVALID NAMESPACE, JAVA SHARED DATA, PUB_SUB, RSRC CONSUMER GROUP
TYPE
VARCHAR2(28)
对象类型,包括:INDEX, TABLE, CLUSTER, VIEW, SET, SYNONYM, SEQUENCE, PROCEDURE, FUNCTION, PACKAGE, PACKAGE BODY, TRIGGER, CLASS, OBJECT, USER, DBLINK, CURSOR, JAVA CLASS, JAVA SHARED DATA, NON-EXISTENT, NOT LOADED, PUB_SUB, REPLICATION OBJECT GROUP, TYPE
SHARABLE_MEM
NUMBER
对象消耗的共享池中的共享内存
LOADS
NUMBER
对象被载入次数。即使对象被置为无效了,这个数字还是会增长。
EXECUTIONS
NUMBER
对象执行次数,但本视图中没有被使用。可以参考视图v$sqlarea中执行次数。
LOCKS
NUMBER
当前锁住这个对象的用户数(如正在调用、执行对象)。
PINS
NUMBER
当前pin住这个对象的用户数(如正在编译、解析对象)。
KEPT
VARCHAR2(3)
对象是否被保持,即调用了DBMS_SHARED_POOL.KEEP来永久将对象pin在内存中。
(YES | NO)
CHILD_LATCH
NUMBER
正在保护该对象的子latch的数量。
· v$sql、v$sqlarea 、v$sqltext这三个视图都可以用于查询共享池中已经解析过的SQL语句及其相关信息。
V$SQL(对应子游标 中的内容 )中列出了共享SQL区中所有语句的信息,它不包含GROUP BY字句,并且为每一条SQL语句中单独存放一条记录;
V$SQL lists statistics on shared SQL areas without the GROUP BY clause and contains one row for each child of the original SQL text entered. Statistics
displayed in V$SQL are normally updated at the end of query execution. However, for long running queries, they are updated every 5 seconds. This makes it easy to see the impact of long running SQL statements while they are still in progress.
V$SQLAREA(对应父游标中的内容)中一条记录显示了一条共享SQL区中的统计信息。它提供了有在内存中、解析过的和准备运行的SQL语句的统计信息;
V$SQLAREA displays statistics on shared SQL areas and contains one row per SQL string. It provides statistics on SQL statements that are in memory, parsed, and ready for execution.
V$SQLTEXT包含了库缓存中所有共享游标对应的SQL语句。它将SQL语句分片显示。
下面介绍一下我常用的V$SQLAREA的结构:
字段
数据类型
说明
SQL_TEXT
VARCHAR2(1000)
游标中SQL语句的前1000个字符。
SHARABLE_MEM
NUMBER
被游标占用的共享内存大小。如果存在多个子游标,则包含所有子游标占用的共享内存大小。
PERSISTENT_MEM
NUMBER
用于一个打开这条语句的游标的生命过程中的固定内存大小。如果存在多个子游标,则包含所有子游标生命过程中的固定内存大小。
RUNTIME_MEM
NUMBER
一个打开这条语句的游标的执行过程中的固定内存大小。如果存在多个子游标,则包含所有子游标执行过程中的固定内存大小。
SORTS
NUMBER
所有子游标执行语句所导致的排序次数。
VERSION_COUNT
NUMBER
缓存中关联这条语句的子游标数。
LOADED_VERSIONS
NUMBER
缓存中载入了这条语句 上下文堆(KGL heap 6) 的子游标数。
OPEN_VERSIONS
NUMBER
打开语句的子游标数。
USERS_OPENING
NUMBER
打开这些子游标的用户数。
FETCHES
NUMBER
SQL语句的fetch数。
EXECUTIONS
NUMBER
所有子游标的执行这条语句次数。
USERS_EXECUTING
NUMBER
通过子游标执行这条语句的用户数。
LOADS
NUMBER
语句被载入和重载入的次数
FIRST_LOAD_TIME
VARCHAR2(19)
语句被第一次载入的时间戳。
INVALIDATIONS
NUMBER
所以子游标的非法次数。
PARSE_CALLS
NUMBER
所有子游标对这条语句的解析调用次数。
DISK_READS
NUMBER
所有子游标运行这条语句导致的读磁盘次数。
BUFFER_GETS
NUMBER
所有子游标运行这条语句导致的读内存次数。
ROWS_PROCESSED
NUMBER
这条语句处理的总记录行数。
COMMAND_TYPE
NUMBER
Oracle命令类型代号。
OPTIMIZER_MODE
VARCHAR2(10)
执行这条的优化器模型。
PARSING_USER_ID
NUMBER
第一次解析这条语句的用户的ID。
PARSING_SCHEMA_ID
NUMBER
第一次解析这条语句所用的schema的ID。
KEPT_VERSIONS
NUMBER
所有被DBMS_SHARED_POOL包标识为保持(Keep)状态的子游标数。
ADDRESS
RAW(4 | 8)
指向语句的地址(Address of the handle to the parent for this cursor)
HASH_VALUE
NUMBER
这条语句在library cache中hash值。
MODULE
VARCHAR2(64)
在第一次解析这条语句是通过调用DBMS_APPLICATION_INFO.SET_MODULE设置的模块名称。
MODULE_HASH
NUMBER
模块的Hash值
ACTION
VARCHAR2(64)
在第一次解析这条语句是通过调用DBMS_APPLICATION_INFO.SET_ACTION设置的动作名称。
ACTION_HASH
NUMBER
动作的Hash值
SERIALIZABLE_ABORTS
NUMBER
所有子游标的事务无法序列化的次数,这会导致ORA-08177错误。
IS_OBSOLETE
VARCHAR2(1)
游标是否被废除(Y或N)。当子游标数太多了时可能会发生。
CHILD_LATCH
NUMBER
为了包含此游标的子latch数。
查看当前会话所执行的语句以及会话相关信息:
SQL> select a.sid||'.'||a.SERIAL#, a.username, a.TERMINAL, a.program, s.sql_text
2 from v$session a, v$sqlarea s
3 where a.sql_address = s.address(+)
4 and a.sql_hash_value = s.hash_value(+)
5 order by a.username, a.sid;
... ...
SQL>· v$sql_plan
视图V$SQL_PLAN包含了library cache中所有游标的执行计划。通过结合v$sqlarea可以查出library cache中所有语句的查询计划。先从v$sqlarea中得到语句的地址,然后在由v$sql_plan查出它的查询计划:
SQL> select lpad(' ', 2*(level-1))||operation "Operation",
2 options "Options",
3 decode(to_char(id), '0', 'Cost='||nvl(to_char(position), 'n/a'), object_name) "Object Name",
4 substr(optimizer, 1, 6) "Optimizer"
5 from v$sql_plan a
6 start with address = 'C0000000FCCDEDA0'
7 and id = 0
8 connect by prior id = a.parent_id
9 and prior a.address = a.address
10 and prior a.hash_value = a.hash_value;
Operation Options Object Name Optimizer
------------------- -------------------- -------------------- ---------
SELECT STATEMENT Cost=0 CHOOSE
NESTED LOOPS
INDEX RANGE SCAN CSS_BL_CNTR_IDX1 ANALYZ
INDEX RANGE SCAN CSS_BKG_BL_ASSN_UQ1 ANALYZ
SQL>· v$librarycache
这个视图包含了关于library cache的性能统计信息,对于共享池的性能调优很有帮助。它是按照命名空间分组统计的,结构如下:
字段
数据类型
说明
NAMESPACE
VARCHAR2(15)
library cache的命名空间
GETS
NUMBER
请求GET该命名空间中对象的次数。
GETHITS
NUMBER
请求GET并在内存中找到了 对象 句柄 的次数(锁定命中, latch命中 )。
GETHITRATIO
NUMBER
请求GET的命中率。
PINS
NUMBER
请求pin住该命名中 对象 的次数。
PINHITS
NUMBER
库对象的所有元数据在内存中被找到的次数( pin命中 )。
PINHITRATIO
NUMBER
Pin命中率。
RELOADS
NUMBER
Pin请求需要从磁盘中载入对象的次数。
INVALIDATIONS
NUMBER
命名空间中的非法对象(由于依赖的对象被修改所导致)数。
DLM_LOCK_REQUESTS
NUMBER
GET请求导致的实例锁的数量。
DLM_PIN_REQUESTS
NUMBER
PIN请求导致的实例锁的数量.
DLM_PIN_RELEASES
NUMBER
请求释放PIN锁的次数。
DLM_INVALIDATION_REQUESTS
NUMBER
GET请求非法实例锁的次数。
DLM_INVALIDATIONS
NUMBER
从其他实例那的得到的非法pin数。
注释:
gets字段是针对找句柄(library cache object handle),而pins 字段是针对找library cache object。
library cache object handle 和 library cache object 两个部分 组成了一个完整的 库对象。
其中,library cache object 这个部分可以被刷出到磁盘上,需要时再重载( reload )到库缓存中。
只有状态为recreatable的chunk才是可以被刷出的对象(位于shared pool的LRU链表上)。
其中PIN的命中率(或未命中率)是我们系统调优的一个重要依据:
SQL> select sum(pins) "hits",
2 sum(reloads) "misses",
3 sum(pins)/(sum(pins)+sum(reloads)) "Hits Ratio"
4 from v$librarycache;
hits misses Hits Ratio
---------- ---------- ----------
84962803 288 0.99999661
SQL>
SQL> select sum(pins) "hits",
2 sum(reloads) "misses",
3 ((sum(reloads)/sum(pins))*100) "Reload%"
4 from v$librarycache;
hits misses Reload%
---------- ---------- ----------
84963808 288 0.00033896
SQL>
当命中率小于99%或未命中率大于1%时,说明系统中硬解析过多( 重载( reload ) 不是意味着将 library cache object 这个部分 从磁盘读取到库缓存中?而是通过重新解析SQL语句来重建(recreate) library cache object 这个部分 ? ),要做系统优化(增加Shared Pool、使用绑定变量、修改cursor_sharing等措施,性能优化不是本文重点,不再赘述)。
· v$library_cache_memory
这个视图显示了各个命名空间中的库缓存内存对象的内存分配情况。一个内存对象是为了高效管理而组织在一起的一组内部内存。一个库对象可能包含多个内存对象。
字段
数据类型
说明
LC_NAMESPACE
VARCHAR2(15)
Library cache命名空间
LC_INUSE_MEMORY_OBJECTS
NUMBER
属于命名空间并正被在共享池使用的内存对象数。
LC_INUSE_MEMORY_SIZE
NUMBER
正在使用的内存对象的大小总(M未单位)。
LC_FREEABLE_MEMORY_OBJECTS
NUMBER
共享池中空闲的内存对象数。
LC_FREEABLE_MEMORY_SIZE
NUMBER
空闲内存对象的大小总和(M为单位)。
· v$sgastat 这个视图前面介绍过,是关于SGA使用情况的统计。其中,关于Shared Pool有详细的统计数据。查看更多关于Oracle内存全面分析(4)-1Oracle的内存架构组成的详细内容...