2.2 Oracle内存结构
Oracle数据库管理与维护实战
Oracle内存存储了数据字典信息(即关于对象、逻辑结构、模式、权限等等的元数据)、缓冲的应用数据、SQL语言、PL/SQL和Java程序数据,以及事物、控制、用户请求信息。图2-3是Oracle内存结构图,Oracle内存主要由SGA(系统全局区,System Global Area)和PGA(程序全局区,Program Global Area)两个区组成,此外还有重做日志缓冲区、大池、Java池等。
2.2.1 系统全局区SGA
SGA是数据库所有用户共享的存储区,它在实例启动时分配,实例关闭时回收。从Oracle 9i起,系统可动态分配SGA大小,即在数据库运行期间可改变SGA中各缓冲的大小,并且及时生效,而Oracle 9i之前版本的数据库,SGA的参数更改后必须将数据库重新启动才生效。在动态调整SGA各缓冲大小时,要注意的是必须低于初始化参数表SGA_MAX_SIZE的值。
SGA的大小对数据库性能有重大影响,实例调整主要是对SGA内各缓冲大小的调整。
1.SGA的自动内存管理
Oracle 10g之前版本的数据库,数据库管理员需要手动配置与SGA有关的大量初始化参数,有SHARED_POOL_SIZE、DB_CACHE_SIZE、JAVA_POOL_SIZE和LARGE_POOL_SIZE等。而从Oracle 10g数据库起,则使用自动数据库管理,数据库管理员只需配置SGA内存总量,即只需配置初始化参数:SGA_TARGET,数据库会自动给SGA内各区分配大小。
自动分配SGA内各区大小时,各区的大小能自动随需求变动。举一个例子,如果总共有1GB大小的内存供SGA使用,并且设置如下初始化参数:
SHARED_POOL_SIZE = 128M
DB_CACHE_SIZE = 896M
如果用SGA自动管理,将SGA_TARGET大小设置成1GB,当一个应用程序需要更大的SHARED_POOL_SIZE共享池,Oracle可以从DB_CACHE_SIZE缓冲区中调整相应的内存给SHARED_POOL_SIZE。仅设置一个SGA总量有一个优势,就是很少会发生内存溢出。
SGA内的参数可以自动调整大小,如共享池、Java池、大池、数据库块缓冲区、Streams池。如果不使用这些缓冲区,这些缓冲区的大小就为0;而一旦要使用某个缓冲区,Oracle就会自动给这部分分配一个合理的大小而不需要人工干预。对用户来说,这些缓冲的调整和分配是透明的。
数据库实例一直会监视SGA内存的使用。实例会用内部视图和一些统计数据来决定如何给各组件最优分配内存。当SGA内存需求变化时,数据库会根据一定的算法,考虑当前和长远的需求来重新分配内存。
但SGA区中还有一部分内存区必需手动调整,这些区有Keep/Recycle缓冲区,相应的初始化参数为DB_KEEP_CACHE_SIZE和DB_RECYCLE_CACHE_SIZE。还有非标准数据块参数,由参数DB_nK_CACHE_SIZE,n = {2, 4, 8, 16, 32}设定。
2.2.2 数据库缓冲区
数据库缓冲区是SGA中的一个高速缓冲区域,用来存储最近从数据文件中读出来的数据块,如表、索引数据块。数据库缓冲区是Oracle提高访问速度的一种有效方法。SGA是所有用户共享的。处理查询时,服务器进程会先从数据库缓冲区查找所需数据块,只有缓冲区中没有时才会访问磁盘。
数据库缓冲区对数据库性能有很大影响。在Oracle 10g数据库之前,数据库缓冲区的大小由DB_BLOCK_BUFFERS参数决定。这个参数可在数据库服务器中的初始化参数文件中设置。数据库性能调优时,调整DB_BLOCK_BUFFERS大小是很重要的一部分。参数DB_BLOCK_SIZE的大小由DB_BLOCK_SIZE和DB_BLOCK_BUFFER相乘得到。DB_BLOCK_SIZE是指缓冲区中可放的物理块的大小,即数据库一次I/O访问的大小。从Oracle 10g数据库后,数据库缓冲区大小由Oracle自动调整。
数据库缓冲区有三种类型:Dirty Buffer、Pinned Buffers、Free Buffer。Dirty Buffer是已被修改需要写回磁盘的数据块,Pinned Buffers是正被访问的数据块,Free Buffer是闲空数据块。
Oracle为了便于管理缓冲区,将缓冲区数据块连成两个队列:写队列和LRU队列。写队列中的数据块排队等待被写回磁盘。Oracle修改了数据之后,并不立即将数据写回磁盘,而是累积到一定数量后才写回。因为访问磁盘I/O速度较慢,如果数据块一被修改就写回到磁盘,会导致频繁访问I/O,造成I/O瓶颈,影响系统性能;而用队列后就可以将多个数据块内容一次性写回磁盘,节省I/O资源。
LRU队列又称为最近最少使用队列,由Free Buffers、Pinned Buffers以及没有加入到写队列的Dirty Buffer组成。所谓最近最少使用队列就是将经常使用的数据块放在队首,将最不经常使用的放在队尾,当有新的数据块要加入时,则最先淘汰最不经常使用的队尾数据块。
当磁盘数据块写入缓冲时,进程首先会在LRU队列搜索Free Buffers,从LRU队尾开始搜索,搜索到足够数量的Free Buffers后,将数据从磁盘读到这些Free Buffers内,将这些Free Buffers放在LRU最不经常使用的队尾。在搜索Free Buffers过程中如果发现队列中有Dirty Buffer,进程就顺便将这Dirty Buffer放入写队列。在搜索Free Buffers的过程中,进程并不会遍历整个LRU队列,而是在搜索了一定数量的数据块后如果仍然没有搜索到足够的Free Buffers块,就停止搜索。这时用户进程会通知DBWR(数据库写进程)将Dirty Buffer数据写回磁盘,以释放空间。
Oracle将数据库缓冲区划成三个区域:KEEP、RECYCLE、DEFAULT。KEEP区中的内容能长期驻留,数据块不会被淘汰出去。这个区域的大小由BUFFER_POOL_KEEP决定。RECYCLE区中存储很少使用的数据,该区的内存可直接回收,其大小由BUFFER_POOL_ RECYCLE决定,剩余的缓冲都属于DEFAULT区。
2.2.3 重做日志缓冲区
用户通过INSERT、UPDATE、DELETE、CREATE、ALTER、DROP等SQL命令更改了数据库后,服务器进程会将这些修改记录到重做日志缓冲区内,这些修改记录也叫重做记录(entry)。数据库发生崩溃后,用户可从重做日志缓冲区读取修改记录恢复数据库。重做日志缓冲区记录了发生修改的块、修改的位置,以及修改后的新值。注意,重做记录只记录每一个修改,并不记录发生修改的块类型,所以不能区分修改是在数据块上还是在回滚或索引段上。
重做日志缓冲区是一个循环缓冲区,缓冲区在被覆盖之前会由后台进程LGWR(日志写入进程)将缓冲内容写入联机重做日志文件,所以Oracle数据库对数据库的每次更改都有记录。重做日志缓冲区的大小由初始参数LOG_BUFFER决定。
2.2.4 共享池
共享池包括库高速缓冲(Library Cache)和数据字典缓冲(Data Dictionary Cache),如图2-3所示。库高速缓冲区又包括共享SQL区、私有SQL区(只在共享服务器内有)、共享PL/SQL区,以及控制结构区。从Oracle 10g开始,数据库能够自动调整共享池大小。
1.共享SQL区
Oracle将执行过的SQL分成两部分:共享SQL区和私有SQL区。当用户执行SQL语句时,Oracle会将最近执行过的SQL语句的文本、编译后的语法分析树和执行计划(指要完成一条SQL语句,Oracle服务器所需具体实施的步骤)存入共享SQL区,而将SQL语句中的变量值存入私有SQL区(在共享服务器中存入私有SQL区,在专用服务器中存入PGA内)。当服务器再次执行相同SQL语句时,服务器进程将不再对这条语句执行语法分析,而是直接执行SQL共享区中已存在的内容。这种方式有利于提高数据库性能。
注意,这里的相同是指语句完全相同,即语句结构相同,大小写相同,变量值相同,否则Oracle服务器会认为不同语句。
2.私有SQL区
私有SQL区中存放的是SQL语句执行时与每一个会话有关的私有数据,如连接变量的值。专用服务器内私有SQL区存在PGA中;共享服务器内私有SQL区存在共享池中。
3.共享PL/SQL区
Oracle处理PL/SQL程序与处理SQL语句的方法相同。执行一个PL/SQL程序单元时,Oracle将程序单元放入共享PL/SQL区,而将程序单元内的SQL语句放到共享SQL区中;当再次需要执行相同的程序单元时,就直接从内存中调用,而不需访问磁盘。
4.控制结构区
这是供实例内部使用的一段内存区,存放了锁、锁存器等方面的信息。
2.2.5 数据字典缓冲区
数据字典缓冲区是共享池的一部分,又称为字典区或行缓冲区,它包含了数据库的结构、用户信息和数据库的表、视图等信息。数据字典缓冲区存储了数据库里所有表和视图的名字、数据库基表的列名和列数据类型以及所有Oracle用户的权限等。
Oracle在分析SQL语句时会频繁地访问数据字典。由于Oracle的频繁访问,内存中专门设置了这个存储区存放数据字典。数据字典缓冲区由Oracle的所有用户进程共享。
数据字典缓冲区同样采用LRU算法来管理,缓冲区大小由数据库内部管理。数据字典缓冲区包含在SGA的共享池内,因此它的大小受共享池大小的限制。
字典缓冲区对数据库性能影响很大。在执行SQL语句过程中,服务器进程会经常访问数据字典缓冲区。如果缓冲区太小,数据库就会因为在缓冲中找不到所需信息而反复通过I/O从磁盘获取,严重影响系统性能。
2.2.6 程序全局区PGA
PGA(程序全局区,Program Global Area)包括会话信息、堆栈空间、排序区,以及游标状态。会话信息存放的是会话的权限、角色和会话性能统计等信息,堆栈空间内存放的是变量、数组和属于会话的其他信息,排序区则是用于排序的一段专用空间,游标状态存放的是当前使用的各种游标的处理阶段。
用户进程连接到Oracle数据后,服务器创建会话,同时分配一个PGA区。一个PGA区由一个Oracle用户进程所使用,不能共享。对专用服务器(一个数据库连接对应一个专用服务器进程),PGA保存堆栈空间信息和会话信息、游标状态、排序区。对共享服务器,PGA只保存堆栈空间信息,会话信息、游标状态、排序区保存在SGA中。PGA的结构如图2-4所示。
初始化参数PGA_AGGREGATE_TARGET可以改变PGA的大小。PGA_AGGREGATE_TARGET是指所有服务器进程PGA占用内存的大小之和,它随服务器进程一起产生和释放。
2.2.7 排序区
排序区(Sort Area)是给所要排序的SQL语句提供的专用内存空间。一个排序区使用的例子如图2-5所示,如执行语句SELECT ENAME,SAL FROM EMP,不需要排序,进程DBWR先将数据从磁盘读到数据库缓冲区,再将数据传给用户进程。而执行SELECT ENAME,SAL FROM EMP ORDER BY SAL时,因为要以SAL排序,所以数据从磁盘读到数据库缓冲后,先以SAL排序,然后将排序结果存入排序区,再将排序结果返回给用户进程。
如果内存不够,Oracle会将数据分割成很多小块放到排序区内,然后对每一小块分别进行排序。排序过程中Oracle将排序区存放不下的数据都存放到磁盘临时段中,最后将这些小段合并起来返回给用户进程。在排序过程中,排序区的大小是动态变化的,但最大不能超过参数SORT_AREA_SIZE的值。
还有一个与排序有关的参数SORT_AREA_RETAINED_SIZE,它决定了排序完成后排序区占用内存的大小。排序区的内存释放后仍然属于服务器进程,并没释放给操作系统。为了提高排序速度,用户应适当调节好SORT_AREA_SIZE大小,使排序尽量在内存中进行。
2.2.8 软件代码区
软件代码区用于存储Oracle系统程序和用户程序正在执行或可以执行的程序代码。软件代码区是只读的,可安装成共享或非共享两种形式。Oracle系统程序是可共享的,用以使多个Oracle用户可存取。用户程序可以设为共享,也可以设为不共享。Oracle系统程序与用户程序不同,前者存放在软件代码区中较为安全的地方。
软件代码区的大小一般是固定的,只有在软件进行修改或重新安装时才能由操作系统改变大小。在支持多例程Oracle中,同一台机器上运行的几个数据库可以使用同一个代码区。
2.2.9 大池
大池(Large Pool)是一个可选内存区,从Oracle 9i版本数据库开始,大池大小由参数LARGE_POOL_SIZE决定;但Oracle 10g版本数据库以后,Oracle自动调整大池大小。在以下几种情况下我们要使用大池:使用共享服务器、执行备份和恢复操作、使用I/O Slaves(要配置DBWR_IO_SLAVES参数)。大池主要为一些需要消耗大量内存的操作提供更大内存空间。
2.2.10 Java池
Java池内存储了Java语句的文本、语法分析表等信息。Java池为Java的命令提供语法分析,从Oracle 9i版本数据库开始,Java池大小由参数JAVA_POOL_SIZE决定;但Oracle 10g版本数据库以后,Oracle自动调整大小。如果要安装Java VM,用户就必须启用Java池。
2.2.11 Streams池
Streams池的主要功能是存放消息(message)。池中存放的消息是共享的,Streams的信息可以从一个数据库传播到另一个数据库。利用Streams池管理消息比原来捕获和管理消息更容易。
用户可以在SGA中分配Streams池。Streams池大小通过初始化参数STREAMS_POOL_SIZE分配。如果没有设置这个参数,Oracle在Streams第一次使用时会自动创建Streams池。