第5章 内存管理
Oracle数据库实例启动时,就需要分配共享内存,启动后台进程,如何分配和设置共享内存参数,对于Oracle来说是非常重要的。不当的内存分配轻则影响性能,重则导致数据库故障,在生产实际中不容忽视。
Oracle数据库所使用的内存主要涉及两个方面:PGA和SGA。本章就Oracle的内存管理问题进行探讨。
5.1 PGA管理
PGA指的是程序全局区(Program Global Area),是服务器进程(Server Process)使用的一块包含数据和控制信息的内存区域,PGA是非共享的内存,在服务器进程启动或创建时分配(在系统运行时,排序、连接等操作也可能需要进一步的PGA分配),并为Server Process排他访问,所以PGA中的数据结构并不需要通过Latch来保护。
5.2.1 什么是PGA
进程的创建通常有两种模式:专用服务器模式(Dedicated Server)及共享服务器模式(Shared Server)。在专用服务器模式下,Oracle会为每个会话启动一个Oracle进程;而在共享服务器模式下,通常在服务器端启动一定数量的服务器进程,然后由多个客户端请求共享同一个Oracle服务进程。通常数据库都应当运行在专用服务器模式下。PGA的内容依专用模式和共享服务器模式而有所不同,但是通常来说,PGA中包含私有SQL区(存放绑定信息、运行时内存结构等)、Session信息等内容。
从内存分配与使用上PGA可以被区分为两个区域:
u? 固定PGA(Fixed PGA)- 固定PGA和固定SGA类似,包含了大量原子变量、小的
数据结构和指向可变PGA的指针,这些变量在源码中定义,在编译时分配,可以被认为是PGA的保留内存。
u? 可变PGA(Variable PGA)- 可变PGA通过具体的内存Heap分配来实现,其空间分
配与使用时可以变化的,通过内部视图X$KSMPP([K]ernel [S]ervice [M]emory [P]GA hea[P])可以查询可变PGA内存的分配和使用情况。PGA的可变区中主要包含会话内存及私有SQL区等。
下图简要说明了PGA的创建过程,当客户端向服务器发送连接请求,服务器监听到客户端请求,在专用服务器模式下,会在服务器端衍生一个Server Process来代理用户的请求,服务器进程进而向实例发起连接,创建会话(CREATE SESSION),而PGA就为Server Proces所分配和使用:
书名书名书名书名书名书名书名书名书名书名书名书名书名书名
可变PGA部分实际上是我们最为关注的PGA部分。虽然PGA的内容对于专用和共享模式会有所不同,但是通常来说,可变PGA又进一步的由以下两部分组成:
u? 会话内存 - Session Memory:用于存放会话的登录信息以及其他相关信息,对于共享
服务器模式,这部分内存是共享而非私有的。
u? 私有的SQL区 - Private SQL Area:Private SQL Area包含绑定变量信息、查询执行
状态信息以及查询工作区等。每个发出SQL查询的会话都拥有一块私有SQL区,对于专用服务器模式,这部分内存在PGA中分配,对于共享服务器模式,这部分内存在SGA中分配。
在这里还需要了解的一个概念是游标(Cursor)。Oracle的应用程序或用户的应用程序执行时,都可能显示或隐式的打开游标(Open Cursor)来进行任务处理,打开游标就需要分配SQL Area。管理私有SQL区是用户进程的责任,而分配和回收则依赖于具体的应用程序,为了防止过度的SQL工作区分配,Oracle通过OPEN_CURSORS参数来限制每个用户进程能够同时打开的游标数量。一个私有SQL区在Cursor打开时分配,当执行结束游标关闭时释放。
简单来说,用户进程的任务执行以及Cursor的使用是PGA内存的主要消耗者,也是我们在进行数据库性能优化时最关心的内容,实际上数据库的活动主要就是Cursor的活动。
进一步的,私有SQL区又由以下两部分组成:
u? 永久区域 - Persistent Area:这个区域包含绑定变量等信息,这部分内存只有在游标
被关闭时才会被释放。
u? 运行时区域 – Runtime Area:这个区域存放了SQL语句运行时所需要的信息,在执
行请求时首先创建,其中包含了查询执行的状态信息(如对于全表扫描,则记录全表扫描的进度等)、SQL work areas(这部分区域在内存密集型请求下分配,如Sort或者Hash-Join等,对于DML语句来说,SQL语句执行完毕就释放该区域,对于查询语句则是在记录返回后或查询取消时释放)
下图简要说明了PGA的整体结构,图示中包含了固有SGA部分,也包含了游标运行时示意:
·2·
第1章 章名章名章名章名章名
5.2.2 UGA与CGA
在上图PGA的介绍中,注意到存在一块成为UGA(User Global Area -用户全局区)的内存区域,这也是可以经常见到的一个名词。UGA由用户会话数据、游标状态和索引区组成。在共享服务器模式下,一个共享服务进程被多个用户进程共享,此时UGA是Shared Pool或Large Pool的一部分,而在专用服务器模式下,UGA则是PGA的一部分。
不考虑Shared Server模式,在Dedicated模式下,PGA与UGA关系,就如同Process和Session的关系,PGA是服务于进程的内存结构,包含进程信息;而UGA是服务于会话的,它包含的是会话的信息。UGA中包含如下信息:
u? 打开游标的永久区和运行区; u? 包的状态信息以及变量信息; u? Java会话的状态信息; u? 启用角色信息、跟踪事件; u? 起作用的NLS参数; u? 所有打开的database links; u? 会话访问控制信息等
和PGA一样,UGA也由两组区组成,固定UGA和可变UGA(或者说UGA堆)。固定UGA包含了大概70个原子变量、小的数据结构以及指向UGA堆的指针。
UGA中的内存分配可以通过内部表X$KSMUP(X$KSMUP - [K]ernel [S]ervice [M]emory [U]GA Hea[P])查询得到。UGA堆包含了存储一些固定表(X$表)的永久内存(依赖于特定参数的设置,如OPEN_CURSORS,OPEN_LINKS和MAX_ENABLED_ROLES)。除此以外,大部分的UGA用于私有SQL区和PL/SQL区。
从Oracle9iR2开始,有一系列新的隐含参数被引入用于控制自动的PGA管理,这其中有一个关键的参数是_use_realfree_heap,当设置这个参数为true时,Oracle会为CGA、UGA单独分配堆,而不从PGA中分配。它的默认值为false,而当设置了pga_aggregate_target后,它
·3·
书名书名书名书名书名书名书名书名书名书名书名书名书名书名
的值自动被改为true:
SQL> SELECT x.ksppinm NAME, y.ksppstvl VALUE, x.ksppdesc describ 2 FROM SYS.x$ksppi x, SYS.x$ksppcv y
3 WHERE x.indx = y.indx AND x.ksppinm LIKE '%&par%'; Enter value for par: realfree
NAME VALUE DESCRIB
------------------------------ -------- ------------------------------------------------ _realfree_heap_max_size 32768 minimum max total heap size, in Kbytes _realfree_heap_free_threshold 4194303 threshold for performing real-free, in Kbytes _realfree_heap_mode 0 mode flags for real-free heap
_use_realfree_heap TRUE use real-free based allocator for PGA memory
_use_realfree_heap是自动管理PGA技术的关键技术变化,realfree代表着实时释放。Oracle9i之前手工管理的PGA的主要问题在于,UGA缺省的在PGA中分配,当会话执行了诸如排序、HASH-JOIN等操作,耗用了大量PGA内存,而当会话执行完毕之后,内存会释放给PGA而不是OS,在很多时候这会导致过度的PGA内存使用(在以前版本PGA内存分配和回收是通过malloc()以及brk()调用来完成的);从Oracle9iR2开始,自动的PGA内存管理当_use_realfree_heap为true时,PGA的内存分配将会通过mmap()调用来实现,这样当调用结束时将不必将内存返回给进程而直接返回给OS,从而实现了更好的PGA内存分配与使用。
通过V$PGASTAT视图可以查询PGA累计释放回OS的内存空间:
SQL> select name,value from v$pgastat where name like '%OS'; NAME VALUE ---------------------------------------- ---------------------- PGA memory freed back to OS 39824916480
下图是UGA的结构示意图:
在PGA的示意图中,还涉及了另外一块内存区域被称为CGA(Call Global Area)-调用
·4·
第1章 章名章名章名章名章名
全局区。与其他的全局区不同,CGA的存在是瞬间的,只存在于调用过程中,而且无论UGA存在于PGA还是SGA,CGA都是PGA的SubHeap。对于实例的一些低层次的调用(Low-Level Call)需要CGA,包括分析SQL语句、执行SQL语句以及获取查询结果都需要使用CGA,在SQL执行过程中的每个递归调用需要一个独立的CGA,在SQL的解析过程中,查询数据字典信息、对SQL进行语法以及语义的解析、SQL的优化以及不同执行计划的评估都需要使用CGA。
当然,调用并不是只通过CGA中的数据结构来工作,实际上调用所需要的大部分的重要数据结构都来自于UGA(如SQL AREA, PL/SQL AREA,Sort Area都存放在UGA中,因为这些结构在调用期间需要一直可用),CGA中只包含了那些调用结束后可以被释放的数据。例如,CGA中包含了Direct I/O BUFFER、递归调用信息、表达式评估的堆栈信息等,此外Java调用内存也在CGA中分配。
5.2.3 PGA管理技术的变迁
在Oracle 9i以前的版本中,PGA由一系列的内存区域组成,这些区域包括主要由*_area_size参数控制。在Oracle8i的环境中,这些参数主要有:sort_area_size、hash_area_size、bitmap_merge_size、create_bitmap_area_size。
可以从数据库中得到这些参数设置的当前值,在Oracle8i中可以通过手工修改sort_area_size、hash_area_size等参数值来控制PGA的使用:
SQL> select * from v$version where rownum <2; BANNER
---------------------------------------------------------------- Oracle8i Enterprise Edition Release 8.1.7.4.0 - 64bit Production SQL> show parameter area_size
NAME TYPE VALUE
------------------------------------ ------- ------------------------------ bitmap_merge_area_size integer 1048576 create_bitmap_area_size integer 8388608 hash_area_size integer 131072 sort_area_size integer 65536
这种独立管理的方式存在一个极大的弊端,以SORT操作为例,如果我们为了使特定的排序操作能够在内存中完成,可能需要设置较大的sort_area_size,但是由于进程的独立PGA内存难于回收和共享,这样可能导致过度的PGA内存消耗,所以合理设置和调整PGA在Oracle9i之前是一件比较复杂的事情;从Oracle9i开始,Oracle提供了一种新的PGA内存管理方法:自动化SQL执行内存管理(Automated SQL Execution Memory Management),也称为自动PGA管理,使用这个新特性,Oracle可以在一个总体PGA使用限制下自动管理和调整SQL内存区,从而大大简化了DBA的工作,同时也提高了数据库的性能。
为实现自动的PGA管理,Oracle引入了几个新的初始化参数:
u? PGA_AGGREGATE_TARGET-此参数用来指定所有session总计可以使用最大PGA
·5·