Oracle内存结构
1. 概述
内存结构是oracle数据库最重要的组成部分之一,在数据库中的操作或多或少都会依赖到内存,是影响数据库性能的重要因素,oracle8i使用静态内存管理,即,SGA内是预先在参数中配置好的,数据库启动时就按这些配置来进行内在分配,oracle10g后引入了动态内存管理,即在数据库运行过程中,内存大小可以在线修改与自动配置。oracle的内存分为2部分:系统全局区SGA(System Global Area)和程序全局区PGA(Program Global Area),oracle总体结构图如下:
2. 系统全局区(SGA)
系统全局区的数据被多个用户共享。当数据库实例启动时,系统全局区内存被自动分配。
查看SGA内存结构如下:
SQL> show sga
Total System Global Area 313159680 bytes
Fixed Size 2212936 bytes
Variable Size 163580856 bytes
Database Buffers 142606336 bytes
Redo Buffers 4759552 bytes
其中Fixed Size是一个固定的值,里面存储了SGA 各部分组件的信息,可以看作引导建立SGA的区域,Variable Size:包含了shared_pool_size、java_pool_size、large_pool_size 、Streams Pool等内存设置,SGA的内存结构主要分为如下7个部分:
1、 固定SGA(Fixed SGA)
2、块缓冲区(Database buffer cache)
3、重做日志缓冲区(Redo log buffer)
4、共享池(Shared pool)
5、大池(Large pool)
6、Java池(Java pool)
7、流池(Stream pool)
查看SGA使用的参数如下,其中sga_max_size的大小是不可以动态调整的
SQL> show parameter sga
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
lock_sga boolean FALSE
pre_page_sga boolean FALSE
sga_max_size big integer 300M
sga_target big integer 300M
2.1 固定SGA(Fixed SGA)
固定SGA:顾名思义,是一段不变的内存区,指向SGA中其他部分,Oracle通过它找到SGA中的其他区,可以简单理解为用于管理的一段内存区。
2.2 块缓冲区(Database buffer cache)
块缓冲区又名数据高速缓冲区,由初始化参数DB_CACHE_SIZE指定大小。工作原理和过程是 LRU(最近最少使用 Least Recently Used )。查询时,Oracle会先把从磁盘读取的数据放入内存供所有用户共享,以后再查询相关数据时不用再次读取磁盘。插入和更新时,Oracle会先在该区中缓存数据,之后批量写到硬盘中。通过块缓冲区,Oracle可以通过内存缓存提高磁盘的I/O性能(注:磁盘I/O的速率是毫米级的,而内存I/O的速率为纳秒级)。
数据高速缓存块由许多大小相等的缓存块组成,这些缓存块的大小和OS块大小相同。这些缓存块分为3大类、
1、脏缓存块( Dirty buffers ):脏缓存块中保存的时被修改过的缓存块。即当一条SQL语句对某个缓存块中的数据进行修改后,该缓存块就被标记为脏缓存块。最后该脏缓存块被DBWn进程写入到硬盘的数据文件中,永久保留起来。
2、命中缓存块( Pinned buffers ):命中缓存块中保存的是最近正在被访问的缓存块。它始终被保留中数据高速缓存中,不会被写入数据文件。
3、空闲缓存块(Free buffers):该缓存块中没有数据,等待被写入数据。oracle从数据文件中读取数据后,寻找空闲缓存块,以便写入其中。
Oracle 通过 2 个列表(DIRTY、LRU)来管理缓存块
1、DIRTY 列表中保存已经被修改但还没有被写入到数据文件中的脏缓存块。
2、LRU列表中保存所有的缓存块(还没有被移动到DIRTY列表中的脏缓存块、空闲缓存块、命中缓存块)。当某个缓存块被访问后,该缓存块就被移动到LRU列表的头部,其他缓存块就向LRU列表的尾部移动。放在最尾部的缓存块就最先被移出LRU列表。
数据高速缓存的工作原理过程是:
A、ORACLE在将数据文件中的数据块复制到数据高速缓存中之前,先在数据高速缓存中找空闲缓存块,以便容纳该数据块。Oracle 将从LRU列表的尾部开始搜索,直到找到所需的空闲缓存块为止。
B、如果先搜索到的是脏缓存块,将该脏缓存块移动到DIRTY列表中,然后继续搜索。如果搜索到的是空闲缓存块,则将数据块写入,然后将该缓存块移动到DIRTY列表的头部。
C、如果能够搜索到足够的空闲缓存块,就将所有的数据块写入到对应的空闲缓存块中。则搜索写入过程结束。
D、如果没有搜索到足够的空闲缓存块,则ORACLE就先停止搜索,而是激活DBWn进程,开始将DIRTY列表中的脏缓存块写入到数据文件中。
E、已经被写入到数据文件中的脏缓存块将变成空闲缓存块,并被放入到LRU列表中。执行完成这个工作后,再重新开始搜索,直到找到足够的空闲缓存块为止。
块缓冲区可以配置1、2或3个缓冲池,默认只有一个:
默认池(Default pool):所有数据默认都在这里缓存,使用LRU算法管理。
保持池(Keep pool):缓存需要多次重用的数据,长期保存内存中,缺省值为0。
回收池(Recycle pool):用来缓存很少重用的数据,用完就释放,缺省值为0。
原来只有一个默认池,所有数据都在这里缓存。这样会产生一个问题:大量很少重用的数据会把需重用的数据“挤出”缓冲区,造成磁盘I/O增加,运行速度下降。后来分出了保持池和回收池根据是否经常重用来分别缓存数据。这三部分内存池需要手动确定大小,并且之间没有共享。例如:保持池中已经满了,而回收池中还有大量空闲内存,这时回收池的内存不会分配给保持池,这些池一般被视为一种非常精细的低级调优设备,只有所有其他调优手段大多用过之后才应考虑使用。
在9i之前,数据缓冲区的大小是由DB_BLOCK_BUFFER确定,之后的版本中,是由参数DB_CACHE_SIZE及DB_nK_CACHE_SIZE确定。不同的表空间可以使用不同的块大小,在创建表空间中加入参数BLOCKSIZE指定该表空间数据块的大小,如果指定的是2k,则对应的缓冲区大小为DB_2K_CACHE_SIZE参数的值,如果指定的是4k,则对应的缓冲区大小为DB_4K_CACHE_SIZE参数的值,以此类推。如果不指定BLOCKSIZE,则默认为参数DB_BLOCK_SIZE的值,对应的缓冲区大小是DB_CACHE_SIZE的值。
缓冲区的设置对性能影响是很突出的。设一个查询要读取的数据块数为A,能够从缓冲区读取到的数据块数为C,需要从磁盘读取的数据块数为D,那么A+C+D,则C/A称为数据缓冲区的命中率。以下语句计算数据缓冲区的命中率:
获取与命中率相关各数据:
Sql>SELECT NAME,value FROM v$sysstat WHERE NAME IN ('session logical reads','physical reads','physical reads direct','physical reads direct (lob)');
命中率计算公式:
Cache hit ratio=1-(physical reads-physical reads direct-physical reads direct (lob))/session logical reads;
命中率sql查询语句:
sql> Select 1-(phy.value-dir.value-lob.value)/log.value from v$sysstat log, v$sysstat phy, v$sysstat dir, v$sysstat LOB where log.name='session logical reads' and phy.name='physical reads' and dir.name='physical reads direct' and lob.name='physical reads direct (lob)';
一般要求命中率在90%以上,如果命中率太低,就应适当调整数据缓冲区的大小。
关于database buffer cache 更详细的介绍可以参考如下博文:
http://blog.csdn.net/gz_xiangjun/article/details/6776761
2.3 重做日志缓冲区(Redo log buffer)
其大小由初始化参数LOG_BUFFER指定,可以在运行期间修改该参数,为了加快访问和速度和工作效率,重做记录并不直接写入重做日志文件中,而是首先从数据高速缓存写入重做日志高速缓存。当重做日志高速缓存中的重做记录达到一定数量或某个时间点时,再由LGWR进程分批写入重做日志文件中(即ORACLE总是先日志后文件或先内存后磁盘)。由于重做日志文件是循环使用的。所以当重做日志文件切换时,还会由ARCn(如果启用了归档日志模式)进程将即将要被覆盖的重做日志文件中的数据写入到归档日志文件中,作为备份。
SHOW PARAMETER LOG_BUFFER; ------查询重做日志缓存的大小
SELECT * FROM V$SYSSTAT; ------查询用户进程等待重做日志缓存的次数。
数据写到重做日志文件之前在这里缓存,在以下情况中触发:
1、每隔3秒
2、缓存达到1MB或1/3满时
3、用户提交时
4、缓冲区的数据写入磁盘前
由初始化参数SHARED_POOL_SIZE指定,默认80MB,可以在运行期间手动修改该参数,共享池中保存了最近执行的SQL语句、PL/SQL过程与包、数据字典信息、锁、以及其他控制结构的信息,是对SQL语句、PL/SQL程序进行语法分析、编译、执行的内存区。共享池分为2大部分:数据字典缓存(data dictionary cache),库缓存(library cache)。
1、数据字典缓存(data dictionary cache),用于存储经常使用的数据字典信息。比如(表的定义、用户名、口令、权限、数据库的结构等)。Oracle运行过程中经常访问该缓存以便解析SQL语句,确定操作的对象是否存在,是否具有权限等。如果不在数据字典缓存中,服务器进程就从保存数据字典信息的数据文件中将其读入到数据字典缓存中。数据字典缓存中保存的是一条一条的记录(就像是内存中的数据库),而其他缓存区中保存的是数据块信息。
调优手段:通过动态视图v$rowcache,
判断公式:(1-sum(getmisses)/(sum(gets)+sum(getmisses)))*100,如果结果>90,证明命中率是合适的,否则需要增大共享池设置;(注:oracle没有直接设置库高速缓冲和数据字典高速缓冲的地方,需要通过调整共享池尺寸实现)
Sql>select (1-sum(getmisses)/(sum(gets)+sum(getmisses)))*100 from v$rowcache
2、库缓存(library cache),大小与OPEN_CURSOR初始化参数相关,ORACLE中每条查询语句都需要打开一个游标,OPEN_CURSOR默认值为300。SQL和PL/SQL代码在执行前会进行“硬解析”来获得执行计划及权限验证等相关辅助操作。“硬解析”很费时间。对于响应时间很短的查询,“硬解析”可以占到全部时间的2/3。对于响应时间较长的统计等操作,“硬解析”所占用的时间比例会下降很多。库缓存的目的就是保存最近解析过的SQL语句、PL/SQL过程和包。这样一来,Oracle在执行一条SQL语句、一段PL/SQL 过程和包之前,首先在“库缓存”中搜索,如果查到它们已经解析过了,就利用“库缓存”中解析结果和执行计划来执行,而不必重新对它们进行解析,显著提高执行速度和工作效率。
调优手段:通过动态视图v$librarycache,判断公式:sum(reloads)/sum(pins),如果结果约等于0,则证明库高速缓存命中率是合适的;如果结果>1,则需要增大共享池设置(shared_pool_size);
Sql>select sum(reloads)/sum(pins) from v$librarycache;
ORACLE将每一条SQL语句分解为可共享、不可共享的两部分。
A、共享SQL区:存储的是最近执行的SQL语句、解析后的语法树和优化后的执行计划。这样以后执行相同的SQL语句就直接利用在共享SQL区中的缓存信息,不必重复语法解析了。Oracle在执行一条新的SQL语句时,会为它在共享SQL区中分配空间,分配的大小取决于SQL语句的复杂度。如果共享SQL区中没有空闲空间,就利用LRU算法,释放被占用的空间。
B、私用SQL区(共享模式时):存储的是在执行SQL语句时与每个会话或用户相关的私有信息。其他会话即使执行相同的SQL语句也不会使用这些信息。比如(绑定变量、环境和会话参数)。
C、PL/SQL过程和包区:工作原理同共享SQL区。但是嵌套在PL/SQL过程和包中的SQL语句、解析后的语法树和优化后的执行计划被存储中共享SQL区中。
D、锁与其他控制结构:存储ORACLE例程内部操作所需的信息。比如(各种锁、闩、寄存器值)。
2.5 大池(Large pool)
大池由初始化参数LARGE_POOL_SIZE确定大小,可以使用ALTER SYSTEM语句来动态改变大池的大小,是可选项,DBA可以根据实际业务需要来决定是否在SGA区中创建大池。如果没有创建大池,则需要大量内存空间的操作将占用共享池的内存, 将对SHARED POOL造成一定的性能影响,而LARGE POOL是起着这种功能隔离作用的一块区域。
ORACLE 需要大量内存的操作有:
A、数据库备份和恢复,如RMAN某些情况下用于磁盘IO缓冲区
B、具有大量排序操作的SQL语句
C、并行化的数据库操作,存放进程间的消息缓冲区
D、共享服务器模式下UGA在大池中分配(如果设置了大池)
2.6Java 池(Java pool)
Java池由初始化参数JAVA_POOL_SIZE确定大小,控制在30-50MB比较合适,用户存放JAVA代码、JAVA语句的语法分析表、JAVA语句的执行方案和进行JAVA程序开发、数据中使用到的JAVA对象资源。可以使用 ALTER SYSTEM SET JAVA_POOL_SIZE=0M SCOPE=SPFILE;语句在服务器初始化参数文件中修改该参数。必须重新启动数据库服务器才能使其生效。
2.7 流池(Stream pool)
9iR2以上增加了“流”技术,10g以上在SGA中增加了流池,流池(或者如果没有配置流池,则是共享池中至多10%的空间)会用于缓存流进程在数据库间移动/复制数据时使用的队列消息。这里并不是使用持久的基于磁盘的队列(这些队列有一些附加的开销),流使用的是内存中的队列。如果这些队列满了,最终还是会写出到磁盘。如果使用内存队列的oracle实例由于某种原因失败了,比如说因为实例错误(软件瘫痪)、掉电或其他原因,就会从重做日志重建这些内存中的队列。
3. 程序全局区(PGA)
程序全局区(PGA)是包含单个用户或服务器数据的控制信息的内存区域。是在用户进程连接到oracle数据库并创建一个会话时,由oracle自动分配的,它是非共享区,主要用于在编程时存储变量与数组。会话结束时,PGA被释放。
谈另一个概念:UGA(User Global Area),即用户全局区,与特定的会话相关联。
专用服务器连接模式,UGA在PGA中分配。共享服务器连接模式,UGA在SGA中的Large Pool中分配。如果采用专用服务器连接模式,PGA中包含UGA,其他区域用来排序,散列和位图合并。简单来讲,PGA=UGA+排序区+散列区+位图合并区。
PGA分两种管理模式(手动、自动):9iR1时默认为手动PGA内存管理,9iR2以后默认为自动PGA内存管理。PGA内存可以动态扩大和回收。
1、手动PGA内存管理,用户指定排序区和散列区所使用的内存,每个连接使用相同的内存。
2、自动PGA内存管理,告诉Oracle可以使用的PGA的总量,由Oraclce根据系统负载决定具体分配。
什么时候使用自动PGA内存管理?什么时候使用手动PGA内存管理?通常白天系统正常运行时适合使用自动PGA内存管理,让Oracle根据当前负载自动管理、分配PGA内存。
夜里用户数少、进行维护的时候可以设定当前会话使用手动PGA内存管理,让当前的维护操作获得尽可能多的内存,加快执行速度。如:服务器平时运行在自动PGA内存管理模式下,夜里有个任务要大表进行排序连接后更新,就可以在该操作session中临时更改为手动PGA内存管理,然后分配大的SORT_AREA_SIZE和HASH_AREA_SIZE(50%甚至80%内存,要确保无其他用户使用),这样能大大加快系统运行速度,又不影响白天高峰期对系统造成的影响。
关于SGA、PGA及各缓存的具体设置管理将在空暇时单独分析。本文结束。