Oracle内存全面分析(2)

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

 

1.1.2.   关于 SGA 的重要视图

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

·        v$sga

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

SQL> select * from v$sga;

NAME                      VALUE

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

Fixed Size              1248428

Variable Size         117441364

Database Buffers      138412032

Redo Buffers            7139328

4 rows selected.

SQL> show sga

Total System Global Area  264241152 bytes

Fixed Size                  1248428 bytes

Variable Size             117441364 bytes

Database Buffers          138412032 bytes

Redo Buffers                7139328 bytes

SQL>

·        v$sgastat

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

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

以下语句可以查询 Shared Pool 空闲率:

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

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

  3      from v$sgastat, v$parameter

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

  5      and v$parameter.name = 'shared_pool_size'

  6      and v$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 * from v$sga_dynamic_free_memory;

CURRENT_SIZE

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

0

SQL> show parameter shared_pool

NAME                                TYPE        VALUE

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

shared_pool_size                    big integer 50331648

SQL> alter system set shared_pool_size=38M;

system altered.

SQL> show parameter shared_pool

NAME                                TYPE        VALUE

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

shared_pool_size                    big integer 41943040

SQL> select * from v$sga_dynamic_free_memory;

CURRENT_SIZE

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

8388608

1.1.3.   数据库缓冲区( Database Buffers

Buffer Cache SGA 区中专门用于存放从数据文件中读取的的数据块拷贝的区域。 Oracle 进程如果发现需要访问的数据块已经在 buffer cache 中,就直接读写内存中的相应区域,而无需读取数据文件,从而大大提高性能(要知道,内存的读取效率是磁盘读取效率的 14000 倍)。 Buffer cache 对于所有 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 中是否存在这个数据块的拷贝。如果发现这个数据块已经存在于 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))/

  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;

    }

}

·        全表扫描

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

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

·        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.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_size db_block_buffers 是不能同时设置的,否则实例启动时会报错。

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

system altered.

SQL> alter system set db_cache_size=128M scope=spfile;

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

·        多种块尺寸系统中的 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 参数。

·        多缓冲池

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

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

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

o        默认的标准缓存池,也就是上面所说的 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 之间只能设置一个。

·        缓冲池建议器

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

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

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值