Oracle内存全面分析(2)

来源:互联网 发布:优化案例 编辑:程序博客网 时间:2024/05/01 16:51

作者: fuyuncat
来源:www.HelloDBA.com

 

1.1.2.   关于SGA的重要视图

要了解和观察SGA的使用情况,并且根据统计数据来处理问题和调整性能,主要有以下的几个系统视图。

·       v$sga

这个视图包括了SGA的的总体情况,只包含两个字段:nameSGA内存区名字)和value(内存区的值,单位为字节)。它的结果和show sga的结果一致,显示了SGA各个区的大小:

SQL> select * from v$sga;

NAME                     VALUE

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

FixedSize             1248428

VariableSize         117441364

DatabaseBuffers      138412032

RedoBuffers           7139328

4 rows selected.

SQL> show sga

Total System Global Area  264241152bytes

FixedSize                 1248428 bytes

VariableSize            117441364 bytes

DatabaseBuffers          138412032 bytes

RedoBuffers               7139328 bytes

SQL>

·       v$sgastat

这个视图比较重要。它记录了关于sga的统计信息。包含三个字段:NameSGA内存区的名字);Bytes(内存区的大小,单位为字节);Pool(这段内存所属的内存池)。

这个视图尤其重要的是,它详细记录了个各个池(Pool)内存分配情况,对于定位4031错误有重要参考价值。

以下语句可以查询SharedPool空闲率:

SQL> select to_number(v$parameter.value)value, v$sgastat.BYTES,

 2        (v$sgastat.bytes/v$parameter.value)*100 "percent free"

  3      fromv$sgastat, v$parameter

  4     where v$sgastat.name= 'free memory'

  5      andv$parameter.name = 'shared_pool_size'

  6      andv$sgastat.pool='shared pool'

  7  ;

    VALUE      BYTES percent free

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

 503316480  141096368 28.033329645

SQL>

·       v$sga_dynamic_components

这个视图记录了SGA各个动态内存区的情况,它的统计信息是基于已经完成了的,针对SGA动态内存区大小调整的操作,字段组成如下:

字段

数据类型

描述

COMPONENT

VARCHAR2(64)

内存区名称

CURRENT_SIZE

NUMBER

当前大小

MIN_SIZE

NUMBER

自从实例启动后的最小值

MAX_SIZE

NUMBER

自从实例启动后的最大值

OPER_COUNT

NUMBER

自从实例启动后的调整次数

LAST_OPER_TYPE

VARCHAR2(6)

最后一次完成的调整动作,值包括:

GROW (增加)

SHRINK (缩小)

LAST_OPER_MODE

VARCHAR2(6)

最后一次完成的调整动作的模式,包括:

MANUAL (手动)

AUTO (自动)

LAST_OPER_TIME

DATE

最后一次完成的调整动作的开始时间

GRANULE_SIZE

NUMBER

GRANULE大小(关于granule后面详细介绍)

·       V$SGA_DYNAMIC_FREE_MEMORY

这个视图只有一个字段,一条记录:当前SGA可用于动态调整SGA内存区的空闲区域大小。它的值相当于(SGA_MAX_SIZE SGA各个区域设置大小的总和)。当设置了SGA_TARGET后,它的值一定为0(为什么就不需要我再讲了吧^_^)。

下面的例子可以很清楚的看到这个视图的作用:

SQL> select * fromv$sga_dynamic_free_memory;

CURRENT_SIZE

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

0

SQL> show parameter shared_pool

NAME                               TYPE        VALUE

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

shared_pool_size                   big integer 50331648

SQL> alter system setshared_pool_size=38M;

system altered.

SQL> show parameter shared_pool

NAME                               TYPE        VALUE

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

shared_pool_size                   big integer 41943040

SQL> select * fromv$sga_dynamic_free_memory;

CURRENT_SIZE

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

8388608

1.1.3.   数据库缓冲区(Database Buffers

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

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

1.1.3.1.    Buffer cache的管理

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

·       Buffer cache的数据块访问

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

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

SQL> select 1-(sum(decode(name,'physical reads', value, 0))/

 2           (sum(decode(name,'db block gets', value, 0))+

 3           (sum(decode(name,'consistent gets', value, 0))))) "Buffer Hit Ratio"

  4  from v$sysstat;

Buffer Hit Ratio

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

      .926185625

1 row selected.

SQL>

根据经验,一个良好性能的系统,这一值一般保持在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进程将脏缓存写入磁盘去。

下面就是oracle用户进程访问一个数据块的伪代码:

user_process_access_block(block)

{

    if (search_lru(block))

    {

       g_cache_hit++;

       return read_block_from_buffer_cache(block);

    }

    else

    {

       g_cache_missed++;

       search_count = 1;

       searched = FALSE;

       set_lru_latch_context();

       buffer_block = get_lru_from_lru();

       do

        {

           if (block == buffer_block)

           {

               set_buffer_block(buffer_block, read_block_from_datafile(block);

               move_buffer_block_to_mru(buffer_block);

               searched = TRUE;

           }

           search_count++;

           buffer_block =get_next_from_lru(buffer_block);

       }while(!searched && search_count < BUFFER_SEARCH_THRESHOLD)

       free_lru_latch_context();

       if (!searched)

        {

           buffer_block = signal_dbw0_write_dirty_buffer();

           set_buffer_block(buffer_block,read_block_from_datafile(block);

           move_buffer_block_to_mru(buffer_block);

        }

       return buffer_block;

    }

}

·       全表扫描

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

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

·       Flush Buffer

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

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

9i,可以用以下语句:

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

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

alter system flush buffer_cache; -- 10g

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

1.1.3.2.           Buffer Cache的重要参数配置

Oracle提供了一些参数用于控制Buffer Cache的大小等特性。下面介绍一下这些参数。

·       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,这个数对于一个系统来说一般是不够用的。

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

SQL> alter system setdb_block_buffers=16384 scope=spfile;

system altered.

SQL> alter system set db_cache_size=128Mscope=spfile;

system altered.

SQL> startup force

ORA-00381: cannot use both new and oldparameters 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

·       多种块尺寸系统中的BufferCache的配置

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

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

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

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

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

·       多缓冲池

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

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

o       回收缓冲池(RecycleBuffer Pool)会立即清除那些不在使用的数据缓存块。它的大小由参数DB_RECYLE_CACHE_SIZE指定;

o       默认的标准缓存池,也就是上面所说的DB_CACHE_SIZE指定。

这三个参数相互之间是独立的。并且他们都只适用于标准块尺寸的数据块。与8i兼容参数DB_BLOCK_BUFFERS相应的,DB_KEEP_CACHE_SIZE对应有BUFFER_POOL_KEEPDB_RECYLE_CACHE_SIZE对应有BUFFER_POOL_RECYCLE。同样,这些参数之间是互斥的,即DB_KEEP_CACHE_SIZEBUFFER_POOL_KEEP之间只能设置一个。

·       缓冲池建议器

9i开始,Oracle提供了一些自动优化工具,用于调整系统配置,提高系统性能。建议器就是其中一种。建议器的作用就是在系统运行过程中,通过监视相关统计数据,给相关配置在不同情况下的性能效果,提供给DBA做决策,以选取最佳的配置。

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

·       其他相关参数

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_PROCESSES1,即只有一个写进程DBW0。这对于一般的系统来说也是足够用。当你的系统的修改数据的任务很重,并且已经影响到性能时,可以调整这个参数。这个参数不要超过CPU数,否则多出的进程也不会起作用,另外,它的最大值不能超过20

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

 

 

原创粉丝点击