Oracle的内存配置与oracle性能息息相关。关于内存的配置,是最影响Oracle性能的配置。内存还直接影响到其他两个重要资源的消耗:CPU和IO. 

先看Oracle内存存储的主要内容是什么:

程序代码(PLSQL、Java);

关于已经连接的会话的信息,包括当前所有活动和非活动会话;

程序运行时必须的相关信息,例如查询计划;

Oracle进程之间共享的信息和相互交流的信息,例如锁;

那些被永久存储在外围存储介质上,被cache在内存中的数据(如redo log条目,数据块)。

每个Oracle数据库都是由Oracle Instance(实例)与数据库(数据文件,控制文件、重做日志文件)组成,其中所谓实例就是用户同数据库交互的媒介,用户通过于一个实例相连来操作数据库。而实例又是由统一的内存结构(SGA,PGA,UGA)和一批内存驻留进程组成。实例在操作系统中用ORACLE_SID来标识,在Oracle中用参数INSTANCE_NAME来标识, 它们两个的值是相同的。数据库启动时,系统首先在服务器内存中分配系统全局区(SGA), 构成了Oracle的内存结构,然后启动若干个常驻内存的操作系统进程,即组成了Oracle的 进程结构,内存区域和后台进程合称为一个Oracle实例。

1、SGA的概念

SGA是一组为系统分配的共享的内存结构,可以包含一个数据库实例的数据或控制信息。如果多个用户连接到同一个数据库实例,在实例的SGA中,数据可以被多个用户共享。   当数据库实例启动时,SGA的内存被自动分配;当数据库实例关闭时,SGA内存被回收。  SGA是占用内存最大的一个区域,同时也是影响数据库性能的重要因素。

SGA区是可读写的。所有登录到实例的用户都能读取SGA中的信息,而在oracle做执行操作时,服务进程会将修改的信息写入SGA区。

SGA主要包括了以下的数据结构:

数据缓冲(Buffer Cache)

重做日志缓冲(Redo Log Buffer)

共享池(Shared Pool)

Java池(Java Pool)

大池(Large Pool)

流池(Streams Pool --- 10g以后才有)

数据字典缓存(Data Dictionary Cache)

其他信息(如数据库和实例的状态信息)

SQL> show sga

Total System Global Area  612368384 bytes

Fixed Size                  1250428 bytes

Variable Size             192940932 bytes

Database Buffers          411041792 bytes

Redo Buffers                7135232 bytes

备注:
(1)SGA 中的数据字典缓存和其他信息会被实例的后台进程所访问,它们在实例启动后就固定在SGA中了,而且不会改变,所以这部分又称为固定SGA(Fixed SGA)。这部分区域的大小一般小于100K。

(2)Shared Pool、Java Pool、Large Pool和Streams Pool这几块内存区的大小是相应系统参数设置而改变的,所以有通称为可变SGA(Variable SGA)。
 
2、SGA参数说明

通过下面的语句查询

SQL> show parameter sga

NAME                  TYPE        VALUE

------------------------------------ ----------- -------

lock_sga                boolean     FALSE

pre_page_sga            boolean     FALSE

sga_max_size            big integer  584M

sga_target               big integer  584M


先对这几个参数做一下说明:

SQL> select name,value ,ISSYS_MODIFIABLE from v$parameter where name like 'sga%';

NAME            VALUE           ISSYS_MOD

--------------- --------------- ---------

sga_max_size    612368384       FALSE

sga_target      612368384       IMMEDIATE

如果ISSYS_MODIFIABLE 返回的是false,说明该参数无法用alter system语句动态修改,需要重启数据库。

所以sga_max_size 是不可以动态调整的。但是我们可以对sga_target 进行动态的调整。 


(1)SGA_MAX_SIZE:

SGA区包括了各种缓冲区和内存池,而大部分都可以通过特定的参数来指定他们的大小。但是,作为一个昂贵的资源,一个系统的物理内存大小是有限。尽管对于CPU的内存寻址来说,是无需关系实际的物理内存大小的,但是过多的使用虚拟内存导致page in/out,会大大影响系统的性能,甚至可能会导致系统crash。所以需要有一个参数来控制SGA使用虚拟内存的最大大小,这个参数就是SGA_MAX_SIZE。

当实例启动后,各个内存区只分配实例所需要的最小大小,在随后的运行过程中,再根据需要扩展他们的大小,而他们的总和大小受到了SGA_MAX_SIZE的限制。

当试图增加一个内存的大小,并且如果这个值导致所有内存区大小总和大于SGA_MAX_SIZE时,oracle会提示错误,不允许修改。

当然,如果在设置参数时,指定区域为spfile时(包括修改SGA_MAX_SIZE本身),是不会受到这个限制的。这样就可能出现这样的情况,在spfile中,SGA各个内存区设置大小总和大于SGA_MAX_SIZE。这时,oracle会如下处理:当实例再次启动时,如果发现SGA各个内存总和大于SGA_MAX_SIZE,它会将SGA_MAX_SIZE的值修改为SGA各个内存区总和的值。

SGA所分配的是虚拟内存,但是,在我们配置SGA时,一定要使整个SGA区都在物理内存中,否则,会导致SGA频繁的页入/页出,会极大影响系统性能。

对于OLTP系统,一般的建议是将SGA_MAX_SIZE 设为物理内存的60%,PGA 设为20%。 但是现在服务器内存是相当大的。 几百G的内存随处可见。60%也就是几百G内存。 显然这样也是不合适的。 所以要根据自己系统来设定设定这个值。这个也就所说的DBA的经验。 这是是需要经验的积累。 


下表的几个数值供参考。

系统内存        SGA_MAX_SIZE值

1G              400-500M
2G              1G
4G              2500M
8G              5G

SGA的实际大小可以通过以下公式估算:

SGA实际大小 = DB_CACHE_SIZE + DB_KEEP_CACHE_SIZE + DB_RECYCLE_CACHE_SIZE + DB_nk_CACHE_SIZE + SHARED_POOL_SIZE + LARGE_POOL_SIZE + JAVA_POOL_SIZE + STREAMS_POOL_SIZE(10g中的新内存池) + LOG_BUFFERS+11K(Redo Log Buffer的保护页) + 1MB + 16M(SGA内部内存消耗,适合于9i及之前版本)

(2)PRE_PAGE_SGA:

oracle实例启动时,会只载入各个内存区最小的大小。而其他SGA内存只作为虚拟内存分配,只有当进程touch到相应的页时,才会置换到物理内存中。我们可以通过设置PRE_PAGE_SGA参数,让实例一启动后,所有SGA都分配到物理内存。

这个参数的默认值为FALSE,即不将全部SGA置入物理内存中。当设置为TRUE时,实例启动会将全部SGA置入物理内存中。它可以使实例启动达到它的最大性能状态,但是,启动时间也会更长(因为为了使所有SGA都置入物理内存中,oracle进程需要touch所有的SGA页)。

SQL> alter system set pre_page_sga=true scope=spfile;

05:29:44 SQL> show parameter pre

NAME                                 TYPE        VALUE

------------------------------------ ----------- ------------------------------

os_authent_prefix                    string      ops$

pre_page_sga                         boolean     FALSE


(3)LOCK_SGA

为了保证SGA都被锁定在物理内存中,而不必页入/页出,可以通过参数LOCK_SGA来控制。这个参数默认值为FALSE,当指定为TRUE时,可以将全部SGA都锁定在物理内存中。当然,有些系统不支持内存锁定,这个参数也就无效了。

05:31:52 SQL> show parameter lock_sga

NAME                                 TYPE        VALUE

------------------------------------ ----------- ------------------------------

lock_sga                             boolean     FALSE

SGA_TARGET

3、自动共享内存管理
Oracle10g中引入的一个非常重要的参数。在10g之前,SGA的各个内存区的大小都需要通过各自的参数指定,并且都无法超过参数指定大小的值,尽管他们之和可能并没有达到SGA的最大限制。此外,一旦分配后,各个区的内存只能给本区使用,相互之间是不能共享的。拿SGA中两个最重要的内存区Buffer Cache和Shared Pool来说,它们两个对实例的性能影响最大,但是就有这样的矛盾存在:在内存资源有限的情况下,某些时候数据被cache的需求非常大,为了提高buffer hit,就需要增加Buffer Cache,但由于SGA有限,只能从其他区“抢”过来——如缩小Shared Pool,增加Buffer Cache;而有时又有大块的PLSQL代码被解析驻入内存中,导致Shared Pool不足,甚至出现4031错误,又需要扩大Shared Pool,这时可能又需要人为干预,从Buffer Cache中将内存夺回来。

10g 以后有了新特性:自动共享内存管理(Automatic Shared Memory Management ASMM)。而控制这一特性的,也就仅仅是这一个参数SGA_TARGE。设置这个参数后,就不需要为每个内存区来指定大小了。SGA_TARGET指定了SGA可以使用的最大内存大小,而SGA中各个内存的大小由Oracle自行控制,不需要人为指定。Oracle可以随时调节各个区域的大小,使之达到系统性能最佳状态的个最合理大小,并且控制他们之和在SGA_TARGET指定的值之内。一旦给SGA_TARGET指定值后(默认为0,即没有启动ASMM),就自动启动了ASMM特性。如果不设置SGA_TARGET,则自动共享内存管理功能被禁止。
 

设置了SGA_TARGET后,以下的SGA内存区就可以由ASMM来自动调整:

共享池(Shared Pool)

Java池(Java Pool)

大池(Large Pool)

数据缓存区(Buffer Cache)

流池(Streams Pool)

对于SGA_TARGET的限制,它的大小是不能超过SGA_MAX_SIZE的大小的。

 

要注意的是:当指定SGA_TARGET大于SGA_MAX_SIZE,实例重启后,SGA_MAX_SIZE就自动变为和SGA_TARGET一样的值了。

SGA_TARGET,它的值可以动态修改(在SGA_MAX_SIZE范围内)。在10g之前,如果需要修改SGA的大小(即修改SGA_MAX_SIZE的值)需要重启实例才能生效。当然,在10g中,修改SGA_MAX_SIZE的值还是需要重启的。但是有了SGA_TARGET后,可以将SGA_MAX_SIZE设置偏大,再根据实际需要调整SGA_TARGET的值(我个人不推荐频繁修改SGA的大小,SGA_TARGET在实例启动时设置好,以后不要再修改)。

SGA_TARGET带来一个重要的好处就是,能使SGA的利用率达到最佳,从而节省内存成本。因为ASMM启动后,Oracle会自动根据需要调整各个区域的大小,大大减少了某些区域内存紧张,而某些区域又有内存空闲的矛盾情况出现。这也同时大大降低了出现4031错误的几率。