oracle内存架构分析,Oracle 内存架构详解

1.1 Database Buffer Cache

Buffer Cache是SGA区中专门用于存放从数据文件中读取的的数据块拷贝的区域。进程如果发现需要访问的数据块已经在buffer cache中,就直接读写内存中的相应区域,而无需读取数据文件,从而大大提高性能(内存的读取效率是磁盘读取效率的14000倍)。Buffer cache对于所有oracle进程都是共享的,即能被所有oracle进程访问。

和Shared Pool一样,buffer cache被分为多个集合,这样能够大大降低多CPU系统中的争用问题。

1.1.1Buffer cache的管理

Oracle对于buffer cache的管理,是通过两个重要的链表实现的:写链表和最近最少使用链表(the Least Recently Used LRU)。写链表所指向的是所有脏数据块缓存(即被进程修改过,但还没有被回写到数据文件中去的数据块,此时缓冲中的数据和数据文件中的数据不一致)。而LRU链表指向的是所有空闲的缓存、pin住的缓存以及还没有来的及移入写链表的脏缓存。空闲缓存中没有任何有用的数据,随时可以使用。而pin住的缓存是当前正在被访问的缓存。LRU链表的两端就分别叫做最近使用端(the Most Recently Used MRU)和最近最少使用端(LRU)。

1)Buffer cache的数据块访问

当一个Oracle进程访问一个缓存时,这个进程会将这块缓存移到LRU链表中的MRU。而当越来越多的缓冲块被移到MRU端,那些已经过时的脏缓冲(即数据改动已经被写入数据文件中,此时缓冲中的数据和数据文件中的数据已经一致)则被移到LRU链表中LRU端。

当一个Oracle用户进程第一次访问一个数据块时,它会先查找buffer cache中是否存在这个数据块的拷贝。如果发现这个数据块已经存在于buffer cache(即命中cache hit),它就直接读从内存中取该数据块。如果在buffer cache中没有发现该数据块(即未命中cache miss),它就需要先从数据文件中读取该数据块到buffer cache中,然后才访问该数据块。命中次数与进程读取次数之比就是我们一个衡量数据库性能的重要指标:buffer hit ratio(buffer命中率),可以通过以下语句获得自实例启动至今的buffer命中率:

SQL>select (1-(sum(decode(name, 'physical reads',value,0))/(sum(decode(name, 'db block gets',value,0))+sum(decode(name,'consistent gets',value,0))))) * 100 "Hit Ratio"from v$sysstat;

Hit Ratio

----------

98.3471481

一个良好性能的系统,命中率一般保持在95%左右。

上面提到,如果未命中(missed),则需要先将数据块读取到缓存中去。这时,oracle进程需要从空闲列表种找到一个适合大小的空闲缓存。如果空闲列表中没有适合大小的空闲buffer,它就会从LRU端开始查找LRU链表,直到找到一个可重用的缓存块或者达到最大查找块数限制。在查找过程中,如果进程找到一个脏缓存块,它将这个缓存块移到写链表中去,然后继续查找。当它找到一个空闲块后,就从磁盘中读取数据块到缓存块中,并将这个缓存块移到LRU链表的MRU端。

当有新的对象需要请求分配buffer时,会通过内存管理模块请求分配空闲的或者可重用的buffer。“free buffer requested”就是产生这种请求的次数;

当请求分配buffer时,已经没有适合大小的空闲buffer时,需要从LRU链表上获取到可重用的buffer。但是,LRU链表上的buffer并非都是立即可重用的,还会存在一些块正在被读写或者已经被别的用户所等待。根据LRU算法,查找可重用的buffer是从链表的LRU端开始查找的,如果这一段的前面存在这种不能理解被重用的buffer,则需要跳过去,查找链表中的下一个buffer。“free buffer inspected”就是被跳过去的buffer的数目。

如果Oracle用户进程达到查找块数限制后还没有找到空闲缓存,它就停止查找LRU链表,并且通过信号同志DBW0进程将脏缓存写入磁盘去。

2)全表扫描

当发生全表扫描(Full Table Scan)时,用户进程读取表的数据块,并将他们放在LRU链表的LRU端(和上面不同,不是放在MRU端)。这样做的目的是为了使全表扫描的数据尽快被移出。因为全表扫描一般发生的频率较低,并且全表扫描的数据块大部分在以后都不会被经常使用到。

而如果你希望全表扫描的数据能被cache住,使之在扫描时放在MRU端,可以通过在创建或修改表(或簇)时,指定CACHE参数。

3)Flush Buffer

回顾一下前面一个用户进程访问一个数据块的过程,如果访问的数据块不在buffer cache中,就需要扫描LRU链表,当达到扫描块数限制后还没有找到空闲buffer,就需要通知DBW0将脏缓存回写到磁盘。如果一个系统中存在大量的脏缓冲,那么就可能导致用户进程访问数据性能下降。

我们可以通过人工干预将所有脏缓冲回写到磁盘去,这就是flush buffer。

在9i,可以用以下语句:

alter system set events = 'immediate trace name flush_cache'; --9i

在10g,可以用以下方式(9i的方式在10g仍然有效):

alter system flush buffer_cache; -- 10g

另外,9i的设置事件的方式可以是针对系统全部的,也可以是对会话的(即将该会话造成的脏缓冲回写)。

1.1.2Buffer Cache的重要参数配置

1)Buffer Cache的大小配置

由于Buffer Cache中存放的是从数据文件中来的数据块的拷贝,因此,它的大小的计算也是以块的尺寸为基数的。而数据块的大小是由参数db_block_size指定的。9i以后,块的大小默认是8K,它的值一般设置为和操作系统的块尺寸相同或者它的倍数。

而参数db_block_buffers则指定了Buffer Cache中缓存块数。因此,buffer cache的大小就等于db_block_buffers * db_block_size。

在9i以后,Oracle引入了一个新参数:db_cache_size。这个参数可以直接指定Buffer Cache的大小,而不需要通过上面的方式计算出。它的默认值48M,这个数对于一个系统来说一般是不够用的。

10G中提供了自动内存管理,通过使用sga_target在在多个组件间自动分配内存以保证最有效的内存使用.如shared pool javapool largepool buffer cache都无需显式设置这些组件的大小,默认都是0,当某个组件需要内存时,可以通过内部自动调整机制请求内存转移.

注意:db_cache_size和db_block_buffers是不能同时设置的,否则实例启动时会报错。

SQL> alter system set db_block_buffers=16384 scope=spfile;

system altered.

SQL> alter system set db_cache_size=20M scope=memory;

system altered.

SQL> startup force

ORA-00381: cannot use both new and old parameters for buffer cache size specification

9i中,推荐使用db_cache_size来指定buffer cache的大小。

在OLTP系统中,对于DB_CACHE_SIZE的设置,推荐配置是:

DB_CACHE_SIZE = SGA_MAX_SIZE/2~SGA_MAX_SIZE*2/3

最后,DB_CACHE_SIZE是可以联机修改的,即实例无需重启,除非增大Buffer Cache导致SGA实际大小大于SGA_MAX_SIZE。

2)多种块尺寸系统中的Buffer Cache的配置

从9i开始,Oracle支持创建不同块尺寸的表空间,并且可以为不同块尺寸的数据块指定不同大小的buffer cache。

9i以后,除了SYSTEM表空间和TEMPORARY表空间必须使用标准块尺寸外,所有其他表空间都可以最多指定四种不同的块尺寸。而标准块尺寸还是由上面的所说的参数db_block_size来指定。而db_cache_size则是标致块尺寸的buffer cache的大小。

非标准块尺寸的块大小可以在创建表空间(CREATE TABLESPACE)是通过BLOCKSIZE参数指定。而不同块尺寸的buffer cache的大小就由相应参数DB_nK_CACHE_SZIE来指定,其中n可以是2,4,8,16或者32。例如,你创建了一个块大小为16K的非标准块尺寸的表空间,你就可以通过设置DB_16K_CACHE_SIZE为来指定缓存这个表空间数据块的buffer cache的大小。

任何一个尺寸的Buffer Cache都是不可以缓存其他尺寸的数据块的。因此,如果你打算使用多种块尺寸用于你的数据库的存储,你必须最少设置DB_CACHE_SIZE和DB_nK_CACHE_SIZE中的一个参数(10g后,指定了SGA_TARGET就可以不需要指定Buffer Cache的大小)。并且,你需要给你要用到的非标准块尺寸的数据块指定相应的Buffer Cache大小。这些参数使你可以为系统指定多达4种不同块尺寸的Buffer Cache。

另外,注意一点,DB_nK_CACHE_SIZE参数不能设定标准块尺寸的缓冲区大小。举例来说,如果DB_BLOCK_SIZE设定为4K,就不能再设定DB_4K_CACHE_SIZE参数。

3)多缓冲池

可以配置不同的buffer cache,可以达到不同的cache数据的目的。比如,可以设置一部分buffer cache缓存过的数据在使用后后马上释放,使后来的数据可以立即使用缓冲池;还可以设置数据进入缓冲池后就被keep住不再释放。部分数据库对象(表、簇、索引以及分区)可以控制他们的数据缓存的行为,而这些不同的缓存行为就使用不同缓冲池。

保持缓冲池(Keep Buffer Pool)用于缓存那些永久驻入内存的数据块。它的大小由参数DB_KEEP_CACHE_SZIE控制;

回收缓冲池(Recycle Buffer Pool)会立即清除那些不在使用的数据缓存块。它的大小由参数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提供了一些自动优化工具,用于调整系统配置,提高系统性能。建议器就是其中一种。建议器的作用就是在系统运行过程中,通过监视相关统计数据,给相关配置在不同情况下的性能效果,提供给DBA做决策,以选取最佳的配置。

9i中,Buffer Cache就有了相应的建议器。参数db_cache_advice用于该建议器的开关,默认值为FALSE(即关)。当设置它为TRUE后,在系统运行一段时间后,就可以查询视图v$db_cache_advice来决定如何使之DB_CACHE_SIZE了。关于这个建议器和视图,我们会在下面的内容中介绍。

5)其他相关参数

DB_BLOCK_LRU_LATCHES

LRU链表作为一个内存对象,对它的访问是需要进行锁(latch)控制的,以防止多个用户进程同时使用一个空闲缓存块。DB_BLOCK_LRU_LATCHES设置了LUR latch的数量范围。Oracle通过一系列的内部检测来决定是否使用这个参数值。如果这个参数没有设置,Oracle会自动为它计算出一个值。一般来说,oracle计算出来的值是比较合理,无需再去修改。

9i以后这个参数是隐含参数。对于隐含参数,我建议在没有得到Oracle支持的情况下不要做修改,否则,如果修改了,Oracle是可以拒绝为你做支持的。

DB_WRITER_PROCESSES

在前面分析Oracle读取Buffer Cache时,提到一个Oracle重要的后台进程DBW0,这个(或这些)进程负责将脏缓存块写回到数据文件种去,称为数据库书写器进程(Database Writer Process)。DB_WRITER_PROCESSES参数配置写进程的个数,各个进程以DBWn区分,其中n>=0,是进程序号。一般情况下,DB_WRITER_PROCESSES = MAX(1, TRUNC(CPU数/8))。也就是说,CPU数小于8时,DB_WRITER_PROCESSES为1,即只有一个写进程DBW0。这对于一般的系统来说也是足够用。当你的系统的修改数据的任务很重,并且已经影响到性能时,可以调整这个参数。这个参数不要超过CPU数,否则多出的进程也不会起作用,另外,它的最大值不能超过20。

DBWn进程除了上面提到的在用户进程读取buffer cache时会被触发,还能被Checkpoint触发(Checkpoint是实例从redo log中做恢复的起始点)。

1.2 Share Pool

SGA中的共享池由库缓存(Library Cache)、字典缓存(Dictionary Cache)、用于并行执行消息的缓冲以及控制结构组成。

Shared Pool的大小由参数SHARED_POOL_SIZE决定。9i中,在32位系统下,这个参数的默认值是8M,而64位系统下的默认值位64M。最大为4G。10g 以后可以通过SGA_TARGET参数来自动调整。

对于Shared Pool的内存管理,是通过修正过的LRU算法表来实现的。

1.2.1库缓存(Library Cache)

Library Cache中包括共享SQL区(Shared SQL Areas)、PL/SQL存储过程以及控制结构(如锁、库缓存句柄)。

任何用户都可以访问共享SQL区(可以通过v$sqlarea访问,随后会介绍这个重要视图)。因此库缓存存在于SGA的共享池中。

1)共享SQL区和私有SQL区

Oracle会为每一条SQL语句运行(每运行一条语句Oracle都会打开一个游标)提供一个共享SQL区(Shared SQL Areas)和私有SQL区(Private SQL Areas属于PGA)。当发现两个(或多个)用户都在运行同一SQL语句时,Oracle会重新组织SQL区,使这些用户能重用共享SQL区。但他们还会在私有SQL区中保存一份这条SQL语句的拷贝。

一个共享SQL区中保存了一条语句的解析树和查询计划。在多用户系统中,Oracle通过为SQL语句使用同一共享SQL区多次运行来节省内存。

当一条新的SQL语句被解析时,Oracle从共享池中分配一块内存来存储共享SQL区。这块内存的大小与这条语句的复杂性相关。如果Shared Pool不够空间分配给共享SQL区,Oracle将释放从LRU链表中查找到最近最少使用的内存块,直到有足够空间给新的语句的共享SQL区。如果Oracle释放的是一个共享SQL区的内存,那么相应的语句在下次执行时需要再次解析并重新分配共享SQL区。而从解析语句到分配共享SQL区是一个比较消耗CPU的工程。这就是为什么我们提倡使用绑定变量的原因了。在没有使用绑定变量时,语句中的变量的数值不同,oracle就视为一条新的语句(9i后可以通过cursor_sharing来控制),重复上面的解析、内存分配的动作,将大大消耗系统资源,降低系统性能。

2)PL/SQL程序单元

Oracle对于PL/SQL程序单元(存储过程、函数、包、匿名PL/SQL块和触发器)的处理过程和对单个的SQL语句的处理过程相似。它会分配一个共享区来存储被解析、编译过的程序单元。同时分配一个私有区域来存放运行程序单元的会话所指定的程序单元的参数值(包括本地变量、全局变量和包变量——这也叫做包的实例化)和用于执行程序所需的内存。如果多个用户运行同一个程序单元,则他们共享同一个共享区域,并且各自保持一份私有区域,用于用户会话中指定的变量值。

而一个PL/SQL程序单元中的每条单个SQL语句的处理过程则和上面描述的SQL语句的处理过程相同。要注意一点,尽管这些语句是从PL/SQL程序单元中来的,但是Oracle还是会为这些语句分配一块共享SQL区,同时为每个用户分配一个相应的私有SQL区。

0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值