[English]
作者:
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中做恢复的起始点)。
上一篇:Oracle内存全面分析(3) | 下一篇:Oracle内存全面分析(1) |
本类中的所有文章 |