Oracle的内存 架构详解.docx
《Oracle的内存 架构详解.docx》由会员分享,可在线阅读,更多相关《Oracle的内存 架构详解.docx(24页珍藏版)》请在冰豆网上搜索。
Oracle的内存架构详解
Oracle的内存配置与oracle性能息息相关。
关于内存的配置,是最影响Oracle性能的配置。
内存还直接影响到其他两个重要资源的消耗:
CPU和IO.
先看Oracle内存存储的主要内容是什么:
程序代码(PLSQL、Java);
关于已经连接的会话的信息,包括当前所有活动和非活动会话;
程序运行时必须的相关信息,例如查询计划;
Oracle进程之间共享的信息和相互交流的信息,例如锁;
那些被永久存储在外围存储介质上,被cache在内存中的数据(如redolog条目,数据块)。
每个Oracle数据库都是由OracleInstance(实例)与数据库(数据文件,控制文件、重做日志文件)组成,其中所谓实例就是用户同数据库交互的媒介,用户通过于一个实例相连来操作数据库。
而实例又是由统一的内存结构(SGA,PGA,UGA)和一批内存驻留进程组成。
实例在操作系统中用ORACLE_SID来标识,在Oracle中用参数INSTANCE_NAME来标识,它们两个的值是相同的。
数据库启动时,系统首先在服务器内存中分配系统全局区(SGA),构成了Oracle的内存结构,然后启动若干个常驻内存的操作系统进程,即组成了Oracle的进程结构,内存区域和后台进程合称为一个Oracle实例。
一.SGA
SGA是一组为系统分配的共享的内存结构,可以包含一个数据库实例的数据或控制信息。
如果多个用户连接到同一个数据库实例,在实例的SGA中,数据可以被多个用户共享。
当数据库实例启动时,SGA的内存被自动分配;当数据库实例关闭时,SGA内存被回收。
SGA是占用内存最大的一个区域,同时也是影响数据库性能的重要因素。
SGA区是可读写的。
所有登录到实例的用户都能读取SGA中的信息,而在oracle做执行操作时,服务进程会将修改的信息写入SGA区。
SGA主要包括了以下的数据结构:
数据缓冲(BufferCache)
重做日志缓冲(RedoLogBuffer)
共享池(SharedPool)
Java池(JavaPool)
大池(LargePool)
流池(StreamsPool---10g以后才有)
数据字典缓存(DataDictionaryCache)
其他信息(如数据库和实例的状态信息)
SQL>showsga
TotalSystemGlobalArea612368384bytes
FixedSize1250428bytes
VariableSize192940932bytes
DatabaseBuffers411041792bytes
RedoBuffers7135232bytes
SGA中的数据字典缓存和其他信息会被实例的后台进程所访问,它们在实例启动后就固定在SGA中了,而且不会改变,所以这部分又称为固定SGA(FixedSGA)。
这部分区域的大小一般小于100K。
SharedPool、JavaPool、LargePool和StreamsPool这几块内存区的大小是相应系统参数设置而改变的,所以有通称为可变SGA(VariableSGA)。
截图出自Oracle11g的架构图。
通过下面的语句查询
SQL>showparametersga
NAMETYPEVALUE
------------------------------------------------------
lock_sgabooleanFALSE
pre_page_sgabooleanFALSE
sga_max_sizebiginteger584M
sga_targetbiginteger584M
先对这几个参数做一下说明:
SQL>selectname,value,ISSYS_MODIFIABLEfromv$parameterwherenamelike'sga%';
NAMEVALUEISSYS_MOD
---------------------------------------
sga_max_size612368384FALSE
sga_target612368384IMMEDIATE
如果ISSYS_MODIFIABLE返回的是false,说明该参数无法用altersystem语句动态修改,需要重启数据库。
所以sga_max_size是不可以动态调整的。
但是我们可以对sga_target进行动态的调整。
SGA_MAX_SIZE:
SGA区包括了各种缓冲区和内存池,而大部分都可以通过特定的参数来指定他们的大小。
但是,作为一个昂贵的资源,一个系统的物理内存大小是有限。
尽管对于CPU的内存寻址来说,是无需关系实际的物理内存大小的,但是过多的使用虚拟内存导致pagein/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(RedoLogBuffer的保护页)+1MB+16M(SGA内部内存消耗,适合于9i及之前版本)
PRE_PAGE_SGA:
oracle实例启动时,会只载入各个内存区最小的大小。
而其他SGA内存只作为虚拟内存分配,只有当进程touch到相应的页时,才会置换到物理内存中。
我们可以通过设置PRE_PAGE_SGA参数,让实例一启动后,所有SGA都分配到物理内存。
这个参数的默认值为FALSE,即不将全部SGA置入物理内存中。
当设置为TRUE时,实例启动会将全部SGA置入物理内存中。
它可以使实例启动达到它的最大性能状态,但是,启动时间也会更长(因为为了使所有SGA都置入物理内存中,oracle进程需要touch所有的SGA页)。
SQL>altersystemsetpre_page_sga=truescope=spfile;
LOCK_SGA
为了保证SGA都被锁定在物理内存中,而不必页入/页出,可以通过参数LOCK_SGA来控制。
这个参数默认值为FALSE,当指定为TRUE时,可以将全部SGA都锁定在物理内存中。
当然,有些系统不支持内存锁定,这个参数也就无效了。
SGA_TARGET
Oracle10g中引入的一个非常重要的参数。
在10g之前,SGA的各个内存区的大小都需要通过各自的参数指定,并且都无法超过参数指定大小的值,尽管他们之和可能并没有达到SGA的最大限制。
此外,一旦分配后,各个区的内存只能给本区使用,相互之间是不能共享的。
拿SGA中两个最重要的内存区BufferCache和SharedPool来说,它们两个对实例的性能影响最大,但是就有这样的矛盾存在:
在内存资源有限的情况下,某些时候数据被cache的需求非常大,为了提高bufferhit,就需要增加BufferCache,但由于SGA有限,只能从其他区“抢”过来——如缩小SharedPool,增加BufferCache;而有时又有大块的PLSQL代码被解析驻入内存中,导致SharedPool不足,甚至出现4031错误,又需要扩大SharedPool,这时可能又需要人为干预,从BufferCache中将内存夺回来。
10g以后有了新特性:
自动共享内存管理(AutomaticSharedMemoryManagementASMM)。
而控制这一特性的,也就仅仅是这一个参数SGA_TARGE。
设置这个参数后,就不需要为每个内存区来指定大小了。
SGA_TARGET指定了SGA可以使用的最大内存大小,而SGA中各个内存的大小由Oracle自行控制,不需要人为指定。
Oracle可以随时调节各个区域的大小,使之达到系统性能最佳状态的个最合理大小,并且控制他们之和在SGA_TARGET指定的值之内。
一旦给SGA_TARGET指定值后(默认为0,即没有启动ASMM),就自动启动了ASMM特性。
如果不设置SGA_TARGET,则自动共享内存管理功能被禁止。
设置了SGA_TARGET后,以下的SGA内存区就可以由ASMM来自动调整:
共享池(SharedPool)
Java池(JavaPool)
大池(LargePool)
数据缓存区(BufferCache)
流池(StreamsPool)
对于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错误的几率。
1.1DatabaseBufferCache
BufferCache是SGA区中专门用于存放从数据文件中读取的的数据块拷贝的区域。
Oracle进程如果发现需要访问的数据块已经在buffercache中,就直接读写内存中的相应区域,而无需读取数据文件,从而大大提高性能(内存的读取效率是磁盘读取效率的14000倍)。
Buffercache对于所有oracle进程都是共享的,即能被所有oracle进程访问。
和SharedPool一样,buffercache被分为多个集合,这样能够大大降低多CPU系统中的争用问题。
1.1.1Buffercache的管理
Oracle对于buffercache的管理,是通过两个重要的链表实现的:
写链表和最近最少使用链表(theLeastRecentlyUsedLRU)。
写链表所指向的是所有脏数据块缓存(即被进程修改过,但还没有被回写到数据文件中去的数据块,此时缓冲中的数据和数据文件中的数据不一致)。
而LRU链表指向的是所有空闲的缓存、pin住的缓存以及还没有来的及移入写链表的脏缓存。
空闲缓存中没有任何有用的数据,随时可以使用。
而pin住的缓存是当前正在被访问的缓存。
LRU链表的两端就分别叫做最近使用端(theMostRecentlyUsedMRU)和最近最少使用端(LRU)。
1)Buffercache的数据块访问
当一个Oracle进程访问一个缓存时,这个进程会将这块缓存移到LRU链表中的MRU。
而当越来越多的缓冲块被移到MRU端,那些已经过时的脏缓冲(即数据改动已经被写入数据文件中,此时缓冲中的数据和数据文件中的数据已经一致)则被移到LRU链表中LRU端。
当一个Oracle用户进程第一次访问一个数据块时,它会先查找buffercache中是否存在这个数据块的拷贝。
如果发现这个数据块已经存在于buffercache(即命中cachehit),它就直接读从内存中取该数据块。
如果在buffercache中没有发现该数据块(即未命中cachemiss),它就需要先从数据文件中读取该数据块到buffercache中,然后才访问该数据块。
命中次数与进程读取次数之比就是我们一个衡量数据库性能的重要指标:
bufferhitratio(buffer命中率),可以通过以下语句获得自实例启动至今的buffer命中率:
SQL>select(1-(sum(decode(name,'physicalreads',value,0))/(sum(decode(name,'dbblockgets',value,0))
+sum(decode(name,'consistentgets',value,0)))))*100"HitRatio"fromv$sysstat;
HitRatio
----------
98.3471481
一个良好性能的系统,命中率一般保持在95%左右。
关于命中率,可以参考blog:
Oracle检查命中率的SQL
上面提到,如果未命中(missed),则需要先将数据块读取到缓存中去。
这时,oracle进程需要从空闲列表种找到一个适合大小的空闲缓存。
如果空闲列表中没有适合大小的空闲buffer,它就会从LRU端开始查找LRU链表,直到找到一个可重用的缓存块或者达到最大查找块数限制。
在查找过程中,如果进程找到一个脏缓存块,它将这个缓存块移到写链表中去,然后继续查找。
当它找到一个空闲块后,就从磁盘中读取数据块到缓存块中,并将这个缓存块移到LRU链表的MRU端。
当有新的对象需要请求分配buffer时,会通过内存管理模块请求分配空闲的或者可重用的buffer。
“freebufferrequested”就是产生这种请求的次数;
当请求分配buffer时,已经没有适合大小的空闲buffer时,需要从LRU链表上获取到可重用的buffer。
但是,LRU链表上的buffer并非都是立即可重用的,还会存在一些块正在被读写或者已经被别的用户所等待。
根据LRU算法,查找可重用的buffer是从链表的LRU端开始查找的,如果这一段的前面存在这种不能理解被重用的buffer,则需要跳过去,查找链表中的下一个buffer。
“freebufferinspected”就是被跳过去的buffer的数目。
如果Oracle用户进程达到查找块数限制后还没有找到空闲缓存,它就停止查找LRU链表,并且通过信号同志DBW0进程将脏缓存写入磁盘去。
2)全表扫描
当发生全表扫描(FullTableScan)时,用户进程读取表的数据块,并将他们放在LRU链表的LRU端(和上面不同,不是放在MRU端)。
这样做的目的是为了使全表扫描的数据尽快被移出。
因为全表扫描一般发生的频率较低,并且全表扫描的数据块大部分在以后都不会被经常使用到。
而如果你希望全表扫描的数据能被cache住,使之在扫描时放在MRU端,可以通过在创建或修改表(或簇)时,指定CACHE参数。
3)FlushBuffer
回顾一下前面一个用户进程访问一个数据块的过程,如果访问的数据块不在buffercache中,就需要扫描LRU链表,当达到扫描块数限制后还没有找到空闲buffer,就需要通知DBW0将脏缓存回写到磁盘。
如果一个系统中存在大量的脏缓冲,那么就可能导致用户进程访问数据性能下降。
我们可以通过人工干预将所有脏缓冲回写到磁盘去,这就是flushbuffer。
在9i,可以用以下语句:
altersystemsetevents='immediatetracenameflush_cache';--9i
在10g,可以用以下方式(9i的方式在10g仍然有效):
altersystemflushbuffer_cache;--10g
另外,9i的设置事件的方式可以是针对系统全部的,也可以是对会话的(即将该会话造成的脏缓冲回写)。
1.1.2BufferCache的重要参数配置
1)BufferCache的大小配置
由于BufferCache中存放的是从数据文件中来的数据块的拷贝,因此,它的大小的计算也是以块的尺寸为基数的。
而数据块的大小是由参数db_block_size指定的。
9i以后,块的大小默认是8K,它的值一般设置为和操作系统的块尺寸相同或者它的倍数。
而参数db_block_buffers则指定了BufferCache中缓存块数。
因此,buffercache的大小就等于db_block_buffers*db_block_size。
在9i以后,Oracle引入了一个新参数:
db_cache_size。
这个参数可以直接指定BufferCache的大小,而不需要通过上面的方式计算出。
它的默认值48M,这个数对于一个系统来说一般是不够用的。
10G中提供了自动内存管理,通过使用sga_target在在多个组件间自动分配内存以保证最有效的内存使用.如sharedpooljavapoollargepoolbuffercache都无需显式设置这些组件的大小,默认都是0,当某个组件需要内存时,可以通过内部自动调整机制请求内存转移.
注意:
db_cache_size和db_block_buffers是不能同时设置的,否则实例启动时会报错。
SQL>altersystemsetdb_block_buffers=16384scope=spfile;
systemaltered.
SQL>altersystemsetdb_cache_size=20Mscope=memory;
systemaltered.
SQL>startupforce
ORA-00381:
cannotusebothnewandoldparametersforbuffercachesizespecification
9i中,推荐使用db_cache_size来指定buffercache的大小。
在OLTP系统中,对于DB_CACHE_SIZE的设置,推荐配置是:
DB_CACHE_SIZE=SGA_MAX_SIZE/2~SGA_MAX_SIZE*2/3
最后,DB_CACHE_SIZE是可以联机修改的,即实例无需重启,除非增大BufferCache导致SGA实际大小大于SGA_MAX_SIZE。
2)多种块尺寸系统中的BufferCache的配置
从9i开始,Oracle支持创建不同块尺寸的表空间,并且可以为不同块尺寸的数据块指定不同大小的buffercache。
9i以后,除了SYSTEM表空间和TEMPORARY表空间必须使用标准块尺寸外,所有其他表空间都可以最多指定四种不同的块尺寸。
而标准块尺寸还是由上面的所说的参数db_block_size来指定。
而db_cache_size则是标致块尺寸的buffercache的大小。
非标准块尺寸的块大小可以在创建表空间(CREATETABLESPACE)是通过BLOCKSIZE参数指定。
而不同块尺寸的buffercache的大小就由相应参数DB_nK_CACHE_SZIE来指定,其中n可以是2,4,8,16或者32。
例如,你创建了一个块大小为16K的非标准块尺寸的表空间,你就可以通过设置DB_16K_CACHE_SIZE为来指定缓存这个表空间数据块的buffercache的大小。
任何一个尺寸的BufferCache都是不可以缓存其他尺寸的数据块的。
因此,如果你打算使用多种块尺寸用于你的数据库的存储,你必须最少设置DB_CACHE_SIZE和DB_nK_CACHE_SIZE中的一个参数(10g后,指定了SGA_TARGET就可以不需要指定BufferCache的大小)。
并且,你需要给你要用到的非标准块尺寸的数据块指定相应的BufferCache大小。
这些参数使你可以为系统指定多达4种不同块尺寸的BufferCache。
另外,注意一点,DB_nK_CACHE_SIZE参数不能设定标准块尺寸的缓冲区大小。
举例来说,如果DB_BLOCK_SIZE设定为4K,就不能再设定DB_4K_CACHE_SIZE参数。
3)多缓冲池
可以配置不同的buffercache,可以达到不同的cache数据的目的。
比如,可以设置一部分buffercache缓存过的数据在使用后后马上释放,使后来的数据可以立即使用缓冲池;还可以设置数据进入缓冲池后就被keep住不再释放。
部分数据库对象(表、簇、索引以及分区)可以控制他们的数据缓存的行为,而这些不同的缓存行为就使用不同缓冲池。
保持缓冲池(KeepBufferPool)用于缓存那些永久驻入内存的数据块。
它的大小由参数DB_KEEP_CACHE_SZIE控制;
回收缓冲池(RecycleBufferPool)会立即清除那些不在使用的数据缓存块。
它的大小由参数DB_RECYLE_CACHE_SIZE指定;
默认的标准缓存池,也就是上面所说的DB_CACHE_SIZE指定。
这三个参数相互之间是独立的。
并且他们都只适用于标准块尺寸的数据块。
与8i兼容参数DB_BLOCK_BUFFERS相应的,DB_KEEP_CACHE_SIZE对应有BUFFER_POOL_KEEP、DB_RECYLE_CACHE_SIZE对应有BUFFER_POOL_RECYCLE。
同样,这些参数之间是互斥的,即DB_KEEP_CACHE_SIZE和BUFFER_POOL_KEEP之间只能设置一个。
4)缓冲池建议器
从9i开始,Oracle提供了一些自动优化工具,用于调整系统配置,提高系统性能。
建议器就是其中一种。
建议器的作用就是在系