对oracle内存自动管理这一块一直是零零碎碎知识的堆积,有时候突然用起来却又想不起来了。
恰逢最近遇到oracle内存争用方面的案例,决定定下心来把相关知识梳理一下。
oracle在10g时候引入了参数sga_target,实现了对sga的自动调整
在11g的时候,引入了参数memory_target,可以实现自动调整所有内存(自动调整SGA和PGA的大小)
这里,我主要从这几个相关参数入手。
memory_max_target:决定着SGA和PGA总和的最大值
memory_target:缺省值是0,当设置非0值时候,表示对SGA和PGA的自动管理,也是SGA和PGA总和的目标值,该值不能大于memory_max_target的值
sga_target:缺省值是0,当设置非0值时候,表示oracle自动管理sga中的各个内存区(database buffer cache,share pool,large pool,java pool,streams pool)
是sga自动管理的目标值
sga_max_size:sga大小的最大值
pga_aggregate_target:此参数用来指定所有session总计可以使用最大PGA内存
------------------------------------------------------------------------------------
对于oracle11g而言,设置了memory_target参数为非零值时候,便表示开启了sga和pga的自动管理,即使sga_target和pga_aggregate_target都设置为0,
oracle依然可以自动管理sga中各个内存区的调整(buffer cache,shared pool,large pool,java pool,streams pool)
1、当memory_target设置成非0值时候
1.1 sga_target和pga_aggregate_target都已经设置了非0值
如果Oracle中已经设置了参数sga_target和pga_aggregate_target,则这两个参数将各自被分配为最小值为他们的目标值。
1.2 sga_target设置大小,pga_aggregate_target没有设置大小
那么pga_aggregate_target初始化值=memory_target-sga_target
1.3 sga_target没有设置大小,pga_aggregate_target设置大小
那么sga_target初始化值=memory_target-pga_aggregate_target
1.4 sga_target和pga_aggregate_target都没有设置大小
Oracle 11g中对这种sga_target和pag_aggregate_target都没有设定大小的情况下,Oracle将对这两个值没有最小值和默认值。Oracle将根据数据库运行状况进行分配大小。
但在数据库启动时会有一个固定比例来分配:
sga_target = memory_target*60%
pga_aggregate_target = memory_target*40%
-----------------------------------------------------------------------------------------
2. 当memory_target设置为0时,(11g的默认值)
11g中默认为0则初始状态下取消了memory_target的作用,完全和10g在内存管理上一致,完全向下兼容。(也有三种情况来对SGA和PGA的大小进行分配)
2.1 sga_target设置值,则自动调节SGA中的shared pool,buffer cache,redo log buffer,java pool,larger pool等内存空间的大小。
PGA则依赖pga_aggregate_target的大小。sga和pga不能自动增长和自动缩小。
2.2 sga_target和pga_aggregate_target都没有设置
SGA中的各组件大小都要明确设定,不能自动调整各组件大小。PGA不能自动增长和收缩。
2.3 memory_max_target设置而memory_target = 0这种情况和10g一样
----------------------------------------------------------------------------------
上面都是一些理论方面的阐述,下面我针对几种参数设置情况进行实验,观察sga,pga等值的变化
----查看oracle当前sga和pga的target值
select a.ksppinm name,b.ksppstvl value
from x$ksppi a,x$ksppcv b
where a.indx = b.indx
and (a.ksppinm like '%sga_target%'
or a.ksppinm like '%pga_aggregate_target%');
1.
---当我将memory_target设置为816m,sga_target=0,pga_aggregate_target=0
SQL> col name format a30
SQL> col value format a30
SQL> select a.ksppinm name,b.ksppstvl value
2 from x$ksppi a,x$ksppcv b
3 where a.indx = b.indx
4 and (a.ksppinm like '%sga_target%'
5 or a.ksppinm like '%pga_aggregate_target%');
NAME VALUE
------------------------------ ------------------------------
sga_target 0
__sga_target 511705088 --约等于memory_target的百分之六十
pga_aggregate_target 0
__pga_aggregate_target 343932928 --约等于memory_target的百分之四十
2.当我将memory_target,sga_target,pga_aggregate_target都设置为0时
SQL> alter system set memory_target=0;
系统已更改。
SQL> alter system set sga_target=0;
系统已更改。
SQL> alter system set pga_aggregate_target=0;
alter system set pga_aggregate_target=0
*
第 1 行出现错误:
ORA-02097: 无法修改参数, 因为指定的值无效
ORA-00093: pga_aggregate_target 必须介于 10M 和 4096G-1 之间
这时候在设置pga时候发生报错了,说明当我选择手动管理内存时候,必须给pga设置一个明确的值,也就是说不能存在memory_target和pga_aggregate_target同时为0的情况
现在,我再执行查询sga,pga的语句:
SQL> select a.ksppinm name,b.ksppstvl value
2 from x$ksppi a,x$ksppcv b
3 where a.indx = b.indx
4 and (a.ksppinm like '%sga_target%'
5 or a.ksppinm like '%pga_aggregate_target%');
NAME VALUE
------------------------------ ------------------------------
sga_target 0
__sga_target 511705088
pga_aggregate_target 343932928
__pga_aggregate_target 343932928
发现,当memory_target被设置为0,pga_aggregate_target的值自动被设为_pga_aggregate_target 的同等大小。
当sga_target被设置为0时候,且由于memory_target也为0,这时候sga中各个内存区大小需要手动管理。由于我库上共享池之类的内存区设置了最小值,现在将他们全部改为自动
管理,且值为0的情况:
SQL> show parameter memory_target
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
memory_target big integer 816M
SQL> show parameter sga_target
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
sga_target big integer 512M
SQL>
SQL> show parameter db_cache
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_cache_advice string ON
db_cache_size big integer 308M
SQL> show parameter pool
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
_shared_io_pool_size big integer 0
buffer_pool_keep string
buffer_pool_recycle string
global_context_pool_size string
java_pool_size big integer 16M
large_pool_size big integer 4M
olap_page_pool_size big integer 0
shared_pool_reserved_size big integer 7969177
shared_pool_size big integer 152M
streams_pool_size big integer 0
SQL>
下面将db_cache_size,java_pool_size,large_pool_size,shared_pool_sized都设为0
SQL> alter system set db_cache_size=0;
系统已更改。
SQL> alter system set large_pool_size=0;
系统已更改。
SQL> alter system set java_pool_size=0;
系统已更改。
SQL> alter system set shared_pool_size=0;
系统已更改。
--查看修改后的结果
SQL> show parameter db_cache_size;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_cache_size big integer 0
SQL> show parameter pool
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
_shared_io_pool_size big integer 0
buffer_pool_keep string
buffer_pool_recycle string
global_context_pool_size string
java_pool_size big integer 0
large_pool_size big integer 0
olap_page_pool_size big integer 0
shared_pool_reserved_size big integer 7969177
shared_pool_size big integer 0
streams_pool_size big integer 0
这时候,再次将memory_target和sga_target设置为0,看看会发生什么呢??
SQL> alter system set memory_target=0;
系统已更改。
SQL> alter system set sga_target=0;
系统已更改。
成功修改为0值,这时候再看看sga中各个内存区大小的情况:
SQL> show parameter db_cache_size
NAME TYPE VALUE
------------------------------------ ----------- ----------------------------
db_cache_size big integer 332M
SQL> show parameter pool
NAME TYPE VALUE
------------------------------------ ----------- ----------------------
_shared_io_pool_size big integer 0
buffer_pool_keep string
buffer_pool_recycle string
global_context_pool_size string
java_pool_size big integer 16M
large_pool_size big integer 4M
olap_page_pool_size big integer 0
shared_pool_reserved_size big integer 7969177
shared_pool_size big integer 152M
streams_pool_size big integer 0
当sga改为手动管理后,如果sga各个内存区没有设置明确大小时,oracle自动给各个内存区分配了大小。具体的分配规则我还不太清楚,需要查查,应该也有个比例公式。
----------------------------------------------------------------
顺便提下,oracle分配内存有个最下单位粒度,叫做granule,这个粒度受隐含参数_ksmg_granule_size控制,
来查看下该参数:
SQL> select
2 x.ksppinm name,
3 y.ksppstvl value,
4 y.ksppstdf isdefault,
5 decode(bitand(y.ksppstvf,7),1,'MODIFIED',4,'SYSTEM_MOD','FALSE') ismod,
6 decode(bitand(y.ksppstvf,2),2,'TRUE','FALSE') isadj
7 from
8 x$ksppi x,
9 x$ksppcv y
10 where
11 x.inst_id = userenv('Instance') and
12 y.inst_id = userenv('Instance') and
13 x.indx = y.indx and
14 x.ksppinm like '%_ksmg_granule_size%'
15 order by
16 translate(x.ksppinm, ' _', ' ');
NAME VALUE ISDEFAULT ISMOD ISADJ
------------------------------ -------------------- --------- ---------- -----
_ksmg_granule_size 4194304 TRUE FALSE FALSE
也就是说我现在库上granule的粒度值为4M,即oracle在分配内存的时候是以4M为单位的,也就意味着比如sga的大小都是4的倍数。
下面我做个实验来验证一下:
SQL> show parameter sga_target
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
sga_target big integer 512M
SQL>
SQL>
SQL> alter system set sga_target=1m; ---------设置sga_target值为1m,然后查看结果,发现sga_target为4M
系统已更改。
SQL> show parameter sga_target
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
sga_target big integer 4M
SQL> alter system set sga_target=6m; ---------接着将sga_target值设置为6m,发现结果是8m,也验证了我之前所说的,oracle分配内存时候是以granule粒度为单位的。
系统已更改。
SQL> show parameter sga_target
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
sga_target big integer 8M
------------------------------------------------------------------------------------------------
稍微总结一下:对于11g而言当设置memory_target参数为非0值的时候,说明oracle自动管理sga和pga,包括sga中各个内存区的自动管理,即使sga_target设置为0
当memory_target设置为0时候,这时候oracle内存管理方式就和10g一样了,通过设置sga_target来确定sga是自动管理还是手动管理。并且当memory_target时候,需要给pga_aggregate_target设置一个非零值。
关于选择内存自动管理还是手动管理,说法各有千秋,就我个人而言,遇到的大多数重要生产库,都是采用手动管理的方式,明确了各个内存区的大小。
而且自动管理还容易出现一些bug,以及内存方面的争用,比如shared pool和buffer cache。