概述
关于内存的配置,是最影响 Oracle性能的配置。内存还直接影响到其他两个重要资源的消耗: CPU 和 IO.
那Oracle 内存存储的主要内容是什么呢?
- 程序代码( PLSQL、 Java);
- 关于已经连接的会话的信息,包括当前所有活动和非活动会话;
- 程序运行时必须的相关信息,例如查询计划;
- Oracle 进程之间共享的信息和相互交流的信息,例如锁;
- 那些被永久存储在外围存储介质上,被 cache 在内存中的数据( 如 redo log 条目,数据块)。
每个 Oracle 数据库都是由 Oracle Instance(实例)与数据库(数据文件,控制文件、重做日志文件)组成,其中所谓实例就是用户同数据库交互的媒介,用户通过于一个实例相连来操作数据库。
而实例又是由统一的内存结构( SGA,PGA, UGA)和一批内存驻留进程组成。
实例在操作系统中用 ORACLE_SID 来标识,在 Oracle 中用参数 INSTANCE_NAME 来标识, 它们两个的值是相同的。
数据库启动时,系统首先在服务器内存中分配系统全局区( SGA), 构成了 Oracle的内存结构,然后启动若干个常驻内存的操作系统进程,即组成了 Oracle 的 进程结构,内存区域和后台进程合称为一个 Oracle 实例。
SGA (System Gloable Area)
架构图
SGA概述
SGA 是一组为系统分配的共享的内存结构,可以包含一个数据库实例的数据或控制信息。
如果多个用户连接到同一个数据库实例,在实例的 SGA 中,数据可以被多个用户共享。
当数据库实例启动时, SGA 的内存被自动分配;当数据库实例关闭时, SGA 内存被回收。
SGA 是占用内存最大的一个区域,同时也是影响数据库性能的重要因素。
SGA 区是可读写的。所有登录到实例的用户都能读取 SGA 中的信息,而在oracle 做执行操作时,服务进程会将修改的信息写入 SGA 区。
SGA 主要包括了以下的数据结构:
- 数据缓冲( Buffer Cache)
- 重做日志缓冲( Redo Log Buffer)
- 共享池( Shared Pool)
- Java 池( Java Pool)
- 大池( Large Pool)
- 流池( Streams Pool — 10g 以后才有)
- 数据字典缓存( Data Dictionary Cache)
- 其他信息(如数据库和实例的状态信息)
SGA 中的数据字典缓存 和其他信息 会被实例的后台进程所访问,它们在实例启动后就固定在 SGA 中了,而且不会改变,所以这部分又称为固定 SGA( Fixed SGA)。这部分区域的大小一般小于 100K。
Shared Pool、 Java Pool、 Large Pool 和 Streams Pool 这几块内存区的大小是相应系统参数设置而改变的,所以有通称为可变 SGA( Variable SGA)。
SGA信息及含义
使用有DBA权限的用户
SQL> show parameter sga
NAME TYPE VALUE
------------------- ----------- --------------------------
lock_sga boolean FALSE
pre_page_sga boolean FALSE
sga_max_size big integer 6256M
sga_target big integer 0
复制
或者查询v$parameter
select a.name ,a.VALUE ,a.ISMODIFIED ,a.DESCRIPTION from v$parameter a where a.NAME like '%sga%';
复制
如果 ISSYS_MODIFIABLE 返回的是 false,说明该参数无法用 alter system语句动态修改,需要重启数据库。
所以 sga_max_size 是不可以动态调整的。但是我们可以对sga_target 进行动态的调整。
SGA_MAX_SIZE
如果发现 SGA 各个内存总和大于 SGA_MAX_SIZE,它会将SGA_MAX_SIZE 的值修改为 SGA 各个内存区总和的值。
SGA 所分配的是虚拟内存,但是,在我们配置 SGA 时,一定要使整个 SGA 区都在物理内存中,否则,会导致 SGA 频繁的页入/页出,会极大影响系统性能。
对于 OLTP 系统, 一般的建议是将 SGA_MAX_SIZE 设为物理内存的 60%,PGA 设为 20%.
下面给出一些参考值:
PRE_PAGE_SGA
这个参数的默认值为FALSE,即不将全部SGA置入物理内存中。当设置为TRUE时,实例启动会将全部SGA置入物理内存中。
它可以使实例启动达到它的最大性能状态,但是,启动时间也会更长(因为为了使所有SGA都置入物理内存中,oracle进程需要touch所有的SGA页)。
SQL> alter system set pre_page_sga=true scope=spfile;
复制
LOCK_SGA
为了保证SGA都被锁定在物理内存中,而不必页入/页出,可以通过参数LOCK_SGA来控制。
这个参数默认值为FALSE,当指定为TRUE时,可以将全部SGA都锁定在物理内存中。
当然,有些系统不支持内存锁定,这个参数也就无效了。
SGA_TARGET
Oracle10g中引入的一个非常重要的参数。
在10g之前,SGA的各个内存区的大小都需要通过各自的参数指定,并且都无法超过参数指定大小的值,尽管他们之和可能并没有达到SGA的最大限制。此外,一旦分配后,各个区的内存只能给本区使用,相互之间是不能共享的。拿SGA中两个最重要的内存区Buffer Cache和Shared Pool来说,它们两个对实例的性能影响最大,但是就有这样的矛盾存在:在内存资源有限的情况下,某些时候数据被cache的需求非常大,为了提高buffer hit,就需要增加Buffer Cache,但由于SGA有限,只能从其他区“抢”过来——如缩小Shared Pool,增加Buffer Cache;而有时又有大块的PLSQL代码被解析驻入内存中,导致Shared Pool不足,甚至出现4031错误,又需要扩大Shared Pool,这时可能又需要人为干预,从Buffer Cache中将内存夺回来。
10g 以后有了新特性:自动共享内存管理(Automatic Shared Memory Management ASMM)。
而控制这一特性的,也就仅仅是这一个参数SGA_TARGE。
设置这个参数后,就不需要为每个内存区来指定大小了。SGA_TARGET指定了SGA可以使用的最大内存大小,而SGA中各个内存的大小由Oracle自行控制,不需要人为指定。Oracle可以随时调节各个区域的大小,使之达到系统性能最佳状态的个最合理大小,并且控制他们之和在SGA_TARGET指定的值之内。
一旦给SGA_TARGET指定值后(默认为0,即没有启动ASMM),就自动启动了ASMM特性。如果不设置SGA_TARGET,则自动共享内存管理功能被禁止。
设置了SGA_TARGET后,以下的SGA内存区就可以由ASMM来自动调整:
- 共享池(Shared Pool)
- Java池(Java Pool)
- 大池(Large Pool)
- 数据缓存区(Buffer Cache)
- 流池(Streams Pool)
对于SGA_TARGET的限制,它的大小是不能超过SGA_MAX_SIZE的大小的。
要注意的是:当指定SGA_TARGET小于SGA_MAX_SIZE,实例重启后,SGA_MAX_SIZE就自动变为和SGA_TARGET一样的值了。
在10g中,修改SGA_MAX_SIZE的值还是需要重启的.
SGA_TARGET带来一个重要的好处就是,能使SGA的利用率达到最佳,从而节省内存成本。因为ASMM启动后,Oracle会自动根据需要调整各个区域的大小,大大减少了某些区域内存紧张,而某些区域又有内存空闲的矛盾情况出现。这也同时大大降低了出现4031错误的几率。
SGA组成
Database Buffer Cache
Buffer Cache是SGA区中专门用于存放从数据文件中读取的的数据块拷贝的区域。Oracle进程如果发现需要访问的数据块已经在buffer cache中,就直接读写内存中的相应区域,而无需读取数据文件,从而大大提高性能.
Buffer cache对于所有oracle进程都是共享的,即能被所有oracle进程访问。
和Shared Pool一样,buffer cache被分为多个集合,这样能够大大降低多CPU系统中的争用问题。
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))/(sum(decode(name, 'db block gets',value,0))
2 +sum(decode(name,'consistent gets',value,0))))) * 100 "Hit Ratio" from v$sysstat;
Hit Ratio
----------
99.6854209
复制
一个良好性能的系统,命中率一般保持在95%左右。
Share Pool
SGA中的共享池由库缓存(Library Cache)、字典缓存(Dictionary Cache)、用于并行执行消息的缓冲以及控制结构组成。
Shared Pool的大小由参数SHARED_POOL_SIZE决定。10g 以后可以通过SGA_TARGET 参数来自动调整。
对于Shared Pool的内存管理,是通过修正过的LRU算法表来实现的。
库缓存(Library Cache)
Library Cache中包括共享SQL区(Shared SQL Areas)、PL/SQL存储过程以及控制结构(如锁、库缓存句柄)。
任何用户都可以访问共享SQL区(可以通过v$sqlarea访问)。因此库缓存存在于SGA的共享池中。
共享SQL区和私有SQL区
Oracle会为每一条SQL语句运行(每运行一条语句Oracle都会打开一个游标)提供一个共享SQL区(Shared SQL Areas)和私有SQL区(Private SQL Areas属于PGA)。当发现两个(或多个)用户都在运行同一SQL语句时,Oracle会重新组织SQL区,使这些用户能重用共享SQL区。但他们还会在私有SQL区中保存一份这条SQL语句的拷贝。
一个共享SQL区中保存了一条语句的解析树和查询计划
从解析语句到分配共享SQL区是一个比较消耗CPU的工程。这就是为什么我们提倡使用绑定变量的原因了。在没有使用绑定变量时,语句中的变量的数值不同,oracle就视为一条新的语句(9i后可以通过cursor_sharing来控制),重复上面的解析、内存分配的动作,将大大消耗系统资源,降低系统性能。
PL/SQL程序单元
Oracle对于PL/SQL程序单元(存储过程、函数、包、匿名PL/SQL块和触发器)的处理过程与SQL的处理方式类似。它会分配一个共享区来存储被解析、编译过的程序单元。
字典缓存(Dictionary Cache)
数据字典是有关于数据库的参考信息、数据库的结构信息和数据库中的用户信息的一组表和视图的集合,如我们常用到的V$视图、DBA_视图都属于数据字典。
共享池的内存管理
当一条SQL语句被提交给Oracle执行,Oracle会自动执行以下的内存分配步骤:
1.Oracle检查共享池,看是否已经存在关于这条语句的共享SQL区。如果存在,这个共享SQL区就被用于执行这条语句。而如果不存在,Oracle就从共享池中分配一块新的共享SQL区给这条语句。同时,无论共享SQL区存在与否,Oracle都会为用户分配一块私有SQL区以保存这条语句相关信息(如变量值)。
2. Oracle为会话分配一个私有SQL区。私有SQL区的所在与会话的连接方式相关。
在以下情况下,Oracle也会将共享SQL区从共享池中释放出来:
- 当使用ANALYZE语句更新或删除表、簇或索引的统计信息时,所有与被分析对象相关的共享SQL区都被从共享池中释放掉。当下一次被释放掉的语句被执行时,又重新在一个新的共享SQL区中根据被更新过的统计信息重新解析。
- 当对象结构被修改过后,与该对象相关的所有共SQL区都被标识为无效(invalid)。在下一次运行语句时再重新解析语句。
- 如果数据库的全局数据库名(Global Database Name)被修改了,共享池中的所有信息都会被清空掉。
- DBA通过手工方式清空共享池:ALTER SYSTEM FLUSH SHARED_POOL;
保留共享池
通过视图
V$SHARED_POOL_RESERVED
可以查到保留池的统计信息。其中字段REQUEST_MISSES记录了没有立即从空闲列表中得到可用的大内存段请求次数。这个值要为0。
因为保留区必须要有足够个空闲内存来适应那些短期的内存请求,而无需将那些需要长期cache住的没被pin住的可重建的段清除。否则就需要考虑增大SHARED_POOL_RESERVED_SIZE了。
Shared Pool的重要参数
$sgastat
复制
SHARED_POOL_SIZE
SHARED_POOL_RESERVED_SIZE:指定了共享池中缓存大内存对象的保留区的大小
_SHARED_POOL_RESERVED_MIN_ALLOC:设置了进入保留区的对象大小的阀值。
Redo Log Buffer重做日志缓存
Redo Log Buffer是SGA中一段保存数据库修改信息的缓存。
.重做条目中包含了由于INSERT、UPDATE、DELETE、CREATE、ALTER或DROP所做的修改操作而需要对数据库重新组织或重做的必须信息。在必要时,重做条目还可以用于数据库恢复。
参数LOG_BUFFER决定了Redo Log Buffer的大小。它的默认值是512K(一般这个大小都是足够的),最大可以到4G。10g中可通过参数自动设置。当系统中存在很多的大事务或者事务数量非常多时,可能会导致日志文件IO增加,降低性能。这时就可以考虑增加LOG_BUFFER。
但是,Redo Log Buffer的实际大小并不是LOB_BUFFER的设定大小。为了保护Redo Log Buffer,oracle为它增加了保护页(一般为11K)
SQL> show parameter log_buffer
NAME TYPE VALUE
------------------------------------ ----------- ------------
log_buffer integer 18317312
SQL> select * from v$sgastat where name = 'log_buffer';
POOL NAME BYTES
------------ -------------------------- ----------
log_buffer 18993152
复制
大池(large pool)
大池是属于SGA的可变区(Variable Area)的,它不属于共享池。
大池中只有两种内存段:空闲(free)和可空闲(freeable)内存段
large pool是没有LRU链表的。
Java池(Java Pool)
Java池也是SGA中的一块可选内存区,它也属于SGA中的可变区。
Java池的内存是用于存储所有会话中特定Java代码和JVM中数据。Java池的使用方式依赖与Oracle服务的运行模式。
Java池的大小由参数JAVA_POOL_SIZE设置。Java Pool最大可到1G。
在Oracle 10g以后,提供了一个新的建议器——Java池建议器——来辅助DBA调整Java池大小。建议器的统计数据可以通过视图V$JAVA_POOL_ADVICE来查询
流池(Streams Pool)
流池是Oracle 10g中新增加的。是为了增加对流的支持。
流池也是可选内存区,属于SGA中的可变区。它的大小可以通过参数STREAMS_POOL_SIZE来指定。
如果没有被指定,oracle会在第一次使用流时自动创建。如果设置了SGA_TARGET参数,Oracle会从SGA中分配内存给流池;
如果没有指定SGA_TARGET,则从buffer cache中转换一部分内存过来给流池。转换的大小是共享池大小的10%。
Oracle同样为流池提供了一个建议器——流池建议器。建议器的统计数据可以通过视图V$STREAMS_POOL_ADVICE查询。
PGA(Program Global Area)
PGA由两组区域组成:固定PGA和可变PGA
它的内存段可以通过视图XKSMPP(另外一个视图XKSMSP可以查到可变SGA的内存段信息,他们的结构相同)查到。
PGA堆包含用于存放
X$
表的的内存(依赖与参数设置,包括DB_FILES、CONTROL_FILES)。
总的来说,PGA的可变区中主要分为以下三部分内容:
- 1)私有SQL区;
- 2)游标和SQL区
- 3)会话内存
UGA ( The User Global Area)
UGA(User Global Area用户全局区)由用户会话数据、游标状态和索引区组成。
PGA是服务于进程的,它包含的是进程的信息;而UGA是服务于会话的,它包含的是会话的信息
CGA ( The Call Global Area)
与其他的全局区不同,CGA(Call Global Area调用全局区)的存在是瞬间的。它只存在于一个调用过程中。对于实例的一些低层次的调用需要CGA,包括:
1)解析一条SQL语句;
2)执行一条SQL语句;
3)取一条SELECT语句的输出值。
Java调用内存也分配在CGA中。它被分为三部分空间:堆空间、新空间和老空间。
软件代码区(Software Code Area)
软件代码区是一部分用于存放那些正在运行和可以被运行的代码(Oracle自身的代码)的内存区。Oracle代码一般存储在一个不同于用户程序存储区的软件代码区,而用户程序存储区是排他的、受保护的区域。