是不是很难准确地分配不同的池所需的内存数?自动共享内存管理特性使得自动将内存分配到最需要的地方去成为可能。
无论您是一个刚入门的 DBA 还是一个经验丰富的 DBA,您肯定至少看到过一次类似以下的错误:ORA-04031:unable to allocate 2216 bytes
of shared memory ("shared pool"... ...
或者这种错误:
ORA-04031:unable to allocate XXXX bytes of shared memory
("large pool","unknown object","session heap","frame")
或者可能这种错误:
ORA-04031:unable to allocate bytes of shared memory ("shared pool",
"unknown object","joxlod:init h", "JOX:ioc_allocate_pal")
第一种错误的原因很明显:分配给共享池的内存不足以满足用户请求。(在某些情况下,原因可能不是池本身的大小,而是未使用绑定变量导致的过多分析造成的碎片,这是我很喜欢的一个主题;但目前让我们把重点放在手头的问题上。)其它的错误分别来自大型池和 Java 池的空间不足。
您需要解决这些错误情况,而不作任何与应用程序相关的修改。那么有哪些方案可选呢?问题是如何在 Oracle 例程所需的所有池之间划分可用的内存。
馅饼怎么分?
正如您所了解的,一个 Oracle 例程的系统全局区域 (SGA) 包含几个内存区域(包括缓冲高速缓存、共享池、Java 池、大型池和重做日志缓冲)。这些池在操作系统的内存空间中占据了固定的内存数;它们的大小由 DBA 在初始化参数文件中指定。
这四个池(数据库块缓冲高速缓存、共享池、Java 池和大型池)几乎占据了 SGA 中所有的空间。(与其它区域相比,重做日志缓冲没有占据多少空间,对我们这里的讨论无关紧要。)作为 DBA,您必须确保它们各自的内存分配是充足的。
假定您决定了这些池的值分别是 2GB、1GB、1GB 和 1GB。您将设置以下初始化参数来为数据库例程规定池的大小。
db_cache_size = 2g
shared_pool_size = 1g
large_pool_size = 1g
java_pool_size = 1g
现在,仔细看一下这些参数。坦白讲,这些值是否准确?我相信您一定会有疑虑。在实际中,没有人能够为这些池指定确切的内存数 — 它们太依赖于数据库内部的处理,而处理的特性随时在变化。
下面是一个示例场景。假定您有一个典型的、大部分属于 OLTP 的数据库,并且为缓冲高速缓存分配的专用内存比为纯 OLTP 数据库(现在已经很少见了)分配的要少。有一天,您的用户放开了一些非常大的全表扫描,以创建当天的结束报表。Oracle9i 数据库为您提供了在线修改内存分配的功能,但由于提供的总物理内存有限,您决定从大型池和 Java 池中取出一些内存:
alter system set db_cache_size = 3g scope=memory;
alter system set large_pool_size = 512m scope=memory;
alter system set java_pool_size = 512m scope=memory;
这个解决方案能够很好地工作一段时间,但是接着夜间的 RMAN 作业(它们使用大型池)开始了,大型池将立即出现内存不足。同样,您从数据库高速缓存中取出一些内存来补充大型池,以挽救这种局面。
RMAN 作业完成,然后启动一个广泛使用 Java 的批处理程序,接着您开始看到与 Java 池相关的错误。因此,您(再次)重新分配池,以满足 Java 池和数据库高速缓存上的内存需求:
alter system set db_cache_size = 2G scope=memory;
alter system set large_pool_size = 512M scope=memory;
alter system set java_pool_size = 1.5G scope=memory;
第二天早上,OLTP 作业恢复在线,这个循环又完全重复!解决这种恶性循环的一种替代方法是永久设置每个池的最大需求。不过,这么做的话,您分配的总的 SGA 可能超出可用的内存 — 从而在为每个池分配的内存数不足时,将增加交换和分页的风险。人工重新分配的方法(虽然不实际)目前看起来很不错。
另一种替代方法是将值设为可接受的最小值。不过,当需求增长且内存不能完全满足时,性能将受到影响。
注意在所有这些示例中,分配给 SGA 的总内存保持不变,而池之间的内存分配根据即时的需求进行修改。如果 RDBMS 将自动探测来自用户的需求并相应地重新分布内存分配,那不是很好吗?
Oracle 数据库 10g 中的自动共享内存管理特性正好能够实现这一目的。您可以决定 SGA 的总大小,然后设置一个名称为 SGA_TARGET 的参数,这个参数决定 SGA 的总大小。SGA 内部的各个池将根据工作负载动态地进行配置。实现自动内存分配仅仅需要 SGA_TARGET 参数的一个非零值。
设置自动共享内存管理
让我们看看该特性是如何工作的。首先,确定 SGA 的总大小。您可以通过确定现在分配了多少内存来估计这个值。
SQL> select sum(value)/1024/1024 from v$sga;
SUM(VALUE)/1024/1024
--------------------
500
此时 SGA 的当前总大小近似为 500MB,并且这个值将变为 SGA_TARGET 的值。接下来,执行语句:
alter system set sga_target = 500M scope=both;
这种方法不需要为各个池设置不同值;因而,您将需要在参数文件中使它们的值为零或全部删除它们。
shared_pool_size = 0
large_pool_size = 0
java_pool_size = 0
db_cache_size = 0
再循环数据库,使这些值生效。这个人工过程还可以通过 Enterprise Manager 10g 实施。从数据库主页中,选择 "Administration" 选项卡,然后选择 "Memory Parameters"。对于人工配置的内存参数,将显示标记为 "Enable" 的按钮,以及所有人工配置的池的值。单击 "Enable" 按钮,启用自动共享内存管理特性。企业管理器将完成剩下的工作。
在配置了自动内存分配之后,您可以利用以下命令检查它们的大小:
04:16:57 SQL> select * from v$sgainfo;
NAME BYTES RES
-------------------------------- ---------- ---
Fixed SGA Size 1220384 No
Redo Buffers 2973696 No
Buffer Cache Size 373293056 Yes
Shared Pool Size 138412032 Yes
Large Pool Size 4194304 Yes
Java Pool Size 4194304 Yes
Streams Pool Size 0 Yes
Granule Size 4194304 No
Maximum SGA Size 524288000 No
Startup overhead in Shared Pool 37748736 No
Free SGA Memory Available 0
11 rows selected.
SQL> select current_size from v$buffer_pool;
CURRENT_SIZE
------------
340
SQL> select pool, sum(bytes)/1024/1024
Mbytes from v$sgastat group by pool;
POOL MBYTES
------------ ----------
java pool 4
large pool 4
shared pool 148
正如您所看到的,所有的池都从 500MB 的总目标大小中自动进行分配。(参见图 1。)缓冲高速缓存大小是 340MB,Java 池是 4MB,大型池是 4MB,共享池是 148MB。它们合起来总的大小为 (340+4+4+148=) 496MB,近似与 500MB 的目标 SGA 的大小相同。