chenhuizhuchz 2018-10-10
Oracle的SGA包括以下几个部分,可以通过show sga命令或者是通过查看v$sga视图来查看SGA的大概组成:
SQL> show sga Total System Global Area 1048576000 bytes Fixed Size 1223392 bytes Variable Size 847250720 bytes Database Buffers 192937984 bytes Redo Buffers 7163904 bytes 17:42:05 SQL> select * from v$sga; NAME VALUE ------------------------------ ------------- Fixed Size 1223392.00 Variable Size 847250720.00 Database Buffers 192937984.00 Redo Buffers 7163904.00
从上面可以大致的看到sga包括一个固定区,一个可变区,一个数据库缓存和一个redo缓存。这些是比较笼统的信息,具体的SGA是由以下几个部分组成的:
- 数据缓冲(Buffer Cache)
- 共享池(Shared Pool)
- 大池(Large Pool)
- Java池(Java Pool)
- 流池(Streams Pool --- 10g以后才有)
- 重做日志缓冲(Redo Log Buffer)
- 其他buffer caches(如KEEP, RECYCLE, and other block sizes cache等)
- Fixed SGA and other internal allocations。
其中,Shared Pool、Java Pool、Large Pool和Streams Pool这几块内存区的大小是相应系统参数设置而改变的,所以有通称为可变SGA(Variable SGA)。最后一个fixed sga部分,一般是在实例启动以后就固定在sga中了,是不会发生变化的,这个部分的区域一般小于100k。
理解了SGA的组成以后,下面就来解释一下有关设置SGA大小的两个参数SGA_MAX_SIZE和SGA_TARGET的含义。
这个参数顾名思义,它用来控制SGA使用虚拟内存的最大大小,这里的虚拟内存的含义可能会有所模糊,先可以这样理解,就是Oracle所能在内存中给SGA分配的最大大小。
解释一下这里“虚拟内存”的含义,确切的应该这样说:实际内存和虚拟内存。我们知道当OS中实际内存不够使用的时候,OS就会去使用虚拟内存。oracle是运行与os之上的一个系统软件,它也是一个程序,它所请求os给它多少内存用来作为其sga(比方说Oracle申请500M内存用作SGA,即SGA_MAX_SIZE=500M),os一般是不会在oracle启动的时候就给它全部的实际内存,而可能只给200M。随着程序的运行,Oracle不断的需要内存,而假设计算机的所有实际内存只有500M,那么很肯定的是OS不可能把全部500M实际内存分配给oracle的sga,可能也最多就给了350M,剩下的150M使用虚拟内存。Oracle的SGA达到500M的时候(即达到SGA_MAX_SIZE指定的大小),实际上这个sga由350M实际内存和150M的虚拟内存组成,如果这个时候Oracle想继续申请内存给SGA使用,那么OS是不会再给其分配内存,因为它已经达到了SGA_MAX_SIZE的最大值。这个例子,虽然比较极端,即使OS实际上比方说有1G内存,Oracle的SGA也未必全部由实际内存组成,可能是由400M实际内存和100M的虚拟内存组成,这是由操作系统的内存管理策略决定的。此时,很显然有个问题,假设我的机器物理内存(实际内存)足够多,如何让Oracle所申请的SGA内存全部在物理内存中呢,因为假设使用了虚拟内存,必定会带来额外的PAGE IN/PAGE OUT的I/O操作,这是很不合算的。这个问题其实就是在物理内存中固定SGA的问题,这要涉及到另外两个参数LOCK_SGA和PRE_PAGE_SGA以及具体操作系统是否支持内存锁定的问题了,对此在这不予讨论。
总之:当实例启动后,各个内存区只分配实例所需要的最小大小,在随后的运行过程中,再根据需要扩展他们的大小,而他们的总和大小受到了SGA_MAX_SIZE的限制。
根据前面的SGA的组成介绍,我们很容易得到一个计算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及之前版本)
而SGA_MAX_SIZE就是它的各个部分内存区都达到定义的最大值的时候的大小之和。
修改SGA_MAX_SIZE的大小,必须要重新启动数据库实例。
这样就可能出现这样的一种情况,在spfile中,SGA各个内存区设置大小总和大于SGA_MAX_SIZE。这时,oracle会如下处理:当实例再次启动时,如果发现SGA各个内存总和大于SGA_MAX_SIZE,它会将SGA_MAX_SIZE的值修改为SGA各个内存区总和的值。
在Oracle 10g中引入了一个非常重要的参数:SGA_TARGET,这也是Oracle 10g的一个新特性。
在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中将内存夺回来。
有了这个新的特性后,SGA中的这种内存矛盾就迎刃而解了。这一特性被称为自动共享内存管理(Automatic Shared Memory Management ASMM),控制这一特性的,就仅仅是这个参数SGA_TARGE。设置这个参数后,你就不需要为每个内存区来指定大小了。SGA_TARGET指定了SGA可以使用的最大内存大小,而SGA中各个内存的大小由Oracle自行控制,不需要人为指定。Oracle可以随时调节各个区域的大小,使之达到系统性能最佳状态的个最合理大小,并且控制他们之和在SGA_TARGET指定的值之内。一旦给SGA_TARGET指定值后(默认为0,即没有启动ASMM),就自动启动了ASMM特性。
当启用Oracle的ASMM新特性以后,也不是SGA的所有区的大小都开始动态起来,只有以下的这些区的内存大小动态共享起来:
* Buffer cache (DB_CACHE_SIZE)* Shared pool (SHARED_POOL_SIZE)* Large pool (LARGE_POOL_SIZE)* Java pool (JAVA_POOL_SIZE)* Streams pool (STREAMS_POOL_SIZE)
而SGA中的其他区域的内存大小仍然是固定不共享的。
SGA_TARGET参数带来了ASMM的新特性,但是它的含义和SGA_MAX_SIZE的一样,也表示SGA最大的大小,于是它也就有了一个限制,那就是它的大小不能大于SGA_MAX_SIZE的大小。请看:
SQL> show parameter sga NAME TYPE VALUE ------------------------------------ --------------- ------------------------------ lock_sga boolean FALSE pre_page_sga boolean FALSE sga_max_size big integer 1000M sga_target big integer 300M 18:29:36 SQL> alter system set sga_target=1100m; alter system set sga_target=1100m * ERROR at line 1: ORA-02097: parameter cannot be modified because specified value is invalid ORA-00823: Specified value of sga_target greater than sga_max_size
从上面这个例子的错误报告中,我们还可以发现一点那就是SGA_TARGET是可以在本实例内动态修改的,请看:
SQL> alter system set sga_target=500m;
System altered.
问题又出现了,既然SGA_TARGET除了带来这个ASMM新特性以及可动态修改以外,它的含义和SGA_MAX_SIZE是一样的,即表示Oracle所能使用的SGA的最大大小。那么这里就有两个最大,虽然有限制SGA_TARGET不能大于SGA_MAX_SIZE大小,可是并没有限制它比它小啊。因此,当SGA_TARGET小于SGA_MAX_SIZE的时候,这两个最大,Oracle该听谁的呢?前面的例子,我们知道SGA_TARGET=500M < SGA_MAX_SIZE=1000M,那么继续试验,请看:
从这个版本来看,这里似乎并没有发生改变。那说明是什么问题呢?很显然,SGA_MAX_SIZE仍然表示SGA的大小的上限值,而SGA_TARGET是SGA的所有组件的大小的最大值之和,即当SGA_TARGET< SGA_MAX_SIZE的时候,oracle就会忽略SGA_MAX_SIZE的值,SGA_TARGET也就成了SGA的在此实例中的上限制,它能动态改变大小,但是不能够大于SGA_MAX_SIZE的值。
有人曾经做过试验,在某些版本中(可能在我的版本之后的版本),当SGA_TARGET < SGA_MAX_SIZE时,实例重启以后SGA_MAX_SIZE就变成SGA_TARGET的大小了。
那么这就说明SGA_TARGET的处理,在不同的版本中是不一样的。
总结:在11g中,这个SGA_TARGET只能设置是等于SGA_MAX_SIZE的大小了,设置比它小,oracle会自动帮你调整,设置比它大,那还是出错。现在可以自己想想,oracle对SGA_TARGET的大小处理在往正确的简单的方向前进中。
SGA_TARGET带来一个重要的好处就是,能使SGA的利用率达到最佳,从而节省内存成本。因为ASMM启动后,Oracle会自动根据需要调整各个区域的大小,大大减少了某些区域内存紧张,而某些区域又有内存空闲的矛盾情况出现。
觉得有用的走波关注哦!