1. PGA的概念以及所包含的内存结构
作为一个复杂的oracle数据库系统来说,每时每刻都要处理不同的用户所提交的SQL语句,获取数
据并返回数据给用户。众所周知,解析SQL语句的工作是在oracle实例中的shared pool所完成的。那么对于每个session来说,其执行SQL语句时所传入的绑定变量放在哪里?而且,对于那些需要执行比较复杂SQL的session来说,比如需要进行排序(sort)或hash连接(hash-join)时,这时这些session所需要的内存空间又从哪里来?另外,还有与每个session相关的一些管理控制信息又放在哪里?对于诸如此类与每个session相关的一些内存的分配问题,oracle通过引入PGA这个内存组件来进行解决。
1.1 PGA的相关概念
PGA按照oracle官方文档解释,叫做程序全局区(Program Global Area),但也有些资料上说还可以理解为进程全局区(Process Global Area)。这两者没有本质的区别,它首先是一个内存区域,其次,该区域中包含了与某个特定服务器进程相关的数据和控制信息。每个进程都具有自己私有的PGA区,这也就意味着,这块区域只能被其所属的进程进入,而不能被其他进程访问,所以在PGA中不需要latch这样的内存结构来保护其中的信息。笼统的来说,PGA里包含了当前进程所使用的有关操作系统资源的信息(比如打开的文件句柄等)以及一些与当前进程相关的一些私有的状态信息。
每个PGA区都包含两部分:
固定PGA部分(Fixed PGA):这部分包含一些小的固定尺寸的变量,以及指向变化PGA部分的指针。
变化PGA部分(Variable PGA):这部分是按照堆(Heap)来进行组织的,所以这部分也叫做PGA堆。可以从X$KSMPP视图中看到有关PGA堆的分布信息。PGA堆中所包含的内存结构包括:
有关一些固定表的永久性内存。
如果session使用的是专用连接方式(dedicated server),则还含有用户全局区(UGA-User Global Area)子堆。如果session使用的是共享连接方式(shared server),则UGA位于SGA中。
调用全局区(CGA-Call Global Area)子堆。
1.2 UGA(用户全局区)的相关概念
UGA是包含与某个特定session相关信息的内存区域,比如session的登录信息以及session私有的SQL区域等。每个UGA也包含两个部分:
固定UGA部分(Fixed UGA):这部分包含一些小的固定尺寸的变量,以及指向变化UGA部分的指针。
变化UGA部分(Variable UGA):这部分也是按照堆来进行组织的,可以从X$KSMUP视图中看到有关UGA堆的分布情况。UGA堆的分布与OPEN_CURSORS、OPEN_LINKS等参数有关系。所谓的游标(cursor)就是放在这里的。UGA堆中所包含的内存结构包括:
私有SQL区域(Private SQL Area):这部分区域包含绑定变量信息以及运行时的内存结构等数据。每一个发出SQL语句的session都有自己的私有SQL区域。这部分区域又可分成两部分:
永久内存区域:这里存放了相同SQL语句多次执行时都需要的一些游标信息,比如绑定变量信息、数据类型转换信息等。这部分内存只有在游标被关闭时才会被释放。
运行时区域:这里存放了当SQL语句运行时所使用的一些信息。这部分区域的大小尺寸依赖于所要执行的SQL语句的类型(sort或hash-join等)和复杂度以及所要处理的数据行的行数以及行的大小。在处理SQL语句时的第一步就是要创建运行时区域,对于DML(INSERT、UPDATE、DELETE)语句来说,SQL语句执行完毕就释放该区域;而对于查询语句(SELECT)来说,则是在所有数据行都被获取并传递给用户以后被释放,或者该查询被取消以后也会被释放。
Session相关的信息。这部分信息包括:
正在使用的包(package)的状态信息。
使用alter session这样的命令所启用的跟踪信息、或者所修改的session级别的优化器参数(optimizer_mode)、排序参数(sort_area_size等)、修改的NLS参数等。
所打开的dblinks。
可使用的角色(roles)等。
从上面可以很明显的看出,我们最需要关注的就是私有SQL区域中的运行时区域了。实际上,从9i
以后,对这部分区域有了一个新的名称:SQL工作区域(SQL Work Area)。SQL工作区域的大小依赖于所要处理的SQL语句的复杂程度而定。如果SQL语句包含诸如group by、Hash-join等这样的操作,则会需要很大的SQL工作区域。实际上,我们调整PGA也就是调整这块区域。后面还会说到这部分内容。
而UGA所处的位置完全由session连接的方式决定:
如果session是通过共享服务器(shared server)方式连到数据库的,则毫无疑问,UGA必须能够被所有进程访问,所以这个时候UGA是从SGA中进行分配的。进一步说,如果SGA中设置了large pool,则UGA从large pool里进行分配;否则,如果没有设置large pool,则UGA只能从shared pool里进行分配了。
如果session是通过专用服务器(dedicated server)方式连到数据库的,则UGA是从进程的PGA中进行分配的。
1.3 CGA(调用全局区)的相关概念
CGA也是一块内存区域,但它是动态的,随着调用(call)的开始而创建,在调用过程中一直存在,直到调用结束时被释放。它存放的是在调用过程中所需要的数据。
我们知道,调用主要包括解析(parse)调用、执行(executive)调用、获取(fetch)调用以及递归SQL调用和PL/SQL调用。从调用的种类可以看出,实际上在调用过程中所需要的数据,比如SQL AREA,PL/SQL AREA和SORT AREA基本都是放在UGA中的,因为这些数据在各个调用之间必须一直存在并可用。而在CGA中只存放了在调用过程中临时需要的数据,比如直接I/O缓存(Direct I/O Buffer)以及堆栈空间等数据结构。因此,没有CGA中的数据结构,调用是无法完成的。
注意,CGA不象UGA可以位于SGA中(以共享服务器模式连接),CGA一定是位于PGA中的。如果当前进程正在运行,则每个PGA中只有一个CGA。如果当前进程没有运行,则该进程的PGA中就没有CGA。
1.4 转储PGA
就象实例中的其他内存结构一样,oracle同样提供了可以将PGA转储到跟踪文件的方法。方法如下:
SQL> alter session set events 'immediate trace name heapdump level n';
其中的level n决定了将哪些内存堆转储到跟踪文件:
Level 1: PGA汇总信息
Level 2: SGA汇总信息
Level 4: UGA汇总信息
Level 8: 当前调用的汇总信息(CGA)
Level 16: 用户调用的汇总信息(CGA)
Level 32: Large pool的汇总信息(LGA)
Level 1025: PGA详细信息
Level 2050: SGA详细信息
Level 5000: UGA 详细信息
Level 8200: 当前调用的详细信息
Level 16400: 用户调用的详细信息
Level 32800: Large pool的详细信息
举例来说,我们转储PGA的汇总信息:
SQL> alter session set events 'immediate trace name heapdump level 1';
到user_dump_dest所定义的目录下,找到跟踪文件并打开,可以看到类似下面的信息:
******************************************************
HEAP DUMP heap name="pga heap" desc=001DB880
extent sz=0x213c alt=84 het=32767 rec=0 flg=2 opc=2
parent=00000000 owner=00000000 nex=00000000 xsz=0x213c
EXTENT 0 addr=03700034
Chunk 370003c sz= 8500 perm "perm " alo=7524
EXTENT 1 addr=0351BC8C
Chunk 351bc94 sz= 9156 freeable "Fixed Uga "
EXTENT 2 addr=03519B3C
Chunk 3519b44 sz= 3764 perm "perm " alo=3764
Chunk 351a9f8 sz= 4196 free " "
Chunk 351ba5c sz= 540 freeable "kopolal dvoid "
……………
Chunk 45e988c sz= 4144 recreate "Alloc environm " latch=00000000
ds 45eade0 sz= 4144 ct= 1
Chunk 45ea8bc sz= 1484 freeable "kpuinit env han"
我们可以看到,其中的红色部分就是在PGA中所包含的固定UGA部分。同时,我们可以使用如下的命令将PGA的子堆也给转储出来,其中9.2以前使用:
SQL> alter session set events 'immediate trace name heapdump_addr level n';
9.2以后使用:
SQL> alter session set events 'immediate trace name heapdump_addr level 1, addr n';
其中的n表示子堆的地址。而子堆的地址可以在PGA的转储文件中找到。比如上面的例子中,我们可以看到这样的一行:
ds 45eade0 sz= 4144 ct= 1
这里的ds 45eade0就是某个子堆的地址,这是个十六进制的数值,于是我们先将其转换为十进制数值:
SQL> select to_number('45eade0','xxxxxxxx') from dual;
TO_NUMBER('45EADE0','XXXXXXXX')
-------------------------------
73313760
这里的73313760就是转储PGA子堆的命令中的n,所以我们可以执行(我的测试库为9.2.0.5):
SQL> ALTER SESSION SET EVENTS 'immediate trace name heapdump_addr level 1, addr 73313760';
2. PGA自动管理及深入研究
2.1 PGA自动管理概述
在9i之前,我们主要是通过设置sort_area_size、hash_area_size等参数值(通常都叫做*_area_size)来管理PGA的使用,不过严格说来,是对PGA中的UGA中的私有SQL区域进行管理,这块内存区域又有个名称叫做SQL工作区域。但是,这里有个问题,就是这些参数都是针对某个session而言的,也就是说设置的参数值对所有连进来的session都生效。在数据库实际运行过程中,总有些session需要的PGA多,而有些session需要的PGA少。如果都设置一个很小的*_area_size,则会使得某些SQL语句运行时由于需要将临时数据交换到磁盘而导致效率低下。而如果都设置一个很大的值,又有可能一方面浪费空间,另一方面,消耗过多内存可能导致操作系统其他组件所需要的内存短缺,而引起数据库整体性能下降。所以如何设置*_area_size的值一直都是DBA很头疼的一个问题。
而从9i起所引入的一个新的特性可以有效的解决这个问题,这个特性就是自动PGA管理。DBA可以根据数据库的负载情况估计所有session大概需要消耗的PGA的内存总和,然后把该值设置为初始化参数pga_aggregate_target的值即可。Oracle会按照每个session的需要为其分配PGA,同时会尽量维持整个PGA的内存总和不超过该参数所定义的值。这样的话,oracle就能尽量避免整个PGA的内存容量异常增长而影响整个数据库的性能。从而,就有效的解决了设置*_area_size所带来的问题。
不过遗憾的是,9i下的PGA自动管理只对专用连接方式有效,对共享连接方式无效。10g以后对两种连接方式都有效。
启用PGA自动管理是很容易的,只要设置两个初始化参数即可。首先,设置workarea_size_policy参数。该参数为auto(也是缺省值)时,表示启用PGA自动管理;而设置该参数为manual时,则表示禁用PGA自动管理,仍然沿用9i之前的方式,即使用*_area_size对PGA进行管理。其次,就是设置pga_aggregate_target了,该参数可以动态进行调整,范围是从10MB到4096GB – 1个字节。
2.2 PGA自动管理深入
PGA中对性能影响最大的就是SQL工作区了。通常说来,SQL工作区越大则对于SQL语句的执行的效率就高,从而对于用户的响应时间就越少。理想情况下,SQL工作区应该可以容纳SQL执行过程中所涉及到的所有输入数据和控制信息。当然,这只是理想情况,现实往往总是不能尽如人意,很多情况下SQL工作区是不能容纳执行SQL所需要的内存空间的,从而不得不交换到临时表空间里。为了衡量执行SQL所需要的内存与实际分配给该SQL的SQL工作区之间的契合程度,oracle将所分配的SQL工作区大小分成三种类型:
optimal尺寸:SQL语句能够完全在所分配的SQL工作区内完成所有的操作。这时的性能最佳。
onepass尺寸:SQL语句需要与磁盘上的临时表空间交互一次才能够在所分配的SQL工作区中完成所有的操作。 multipass尺寸:由于SQL工作区过小,从而导致SQL语句需要与磁盘上的临时表空间交互多次才能完成所有的操作。这个时候的性能将急剧下降。
当系统整体负载不大时,oracle倾向于为每个session的PGA分配optimal尺寸大小的SQL工作区。
而随着负载上升,比如连接的session逐渐增多导致同时执行的SQL语句越来越多时,oracle就会倾向于为每个session的PGA分配onepass尺寸大小的SQL工作区,甚至是multipass尺寸的SQL工作区了。
那么,PGA自动管理机制在内部到底是如何实现的呢?很遗憾,oracle官方并没有给出说明文档。其实这本身也说明了,PGA自动管理的内部算法会随着版本升级而发生变化。不过,知其然而不知其所以然,总是会让诸如我等之类的技术人员感觉如梗在喉。还好,曾经就有一些专门做oracle优化的公司发布的文档中介绍了PGA内部的实现原理,我想这可能是oracle公司透露给这些公司的。这里就做些简单的介绍,不过记住,这里所描述的PGA自动管理的原理并不一定就是将来版本的原理,只能说是截至到9.2的PGA自动管理的原理。
PGA自动管理是采用名为“循环反馈(feedback loop)”的算法来实现的。如下图所示。
上图中,当开始处理一条SQL时,oracle会使用本地内存管理器(local memory manager)对该SQL语句相关的work area profile进行注册。work area profile是一组元数据,描述了该SQL语句所需要的工作区的所有特征,包括该SQL的类型(sort还是hash-join等)、该SQL语句的并行度、所需要的内存等信息。它是SQL语句操作与内存管理器之间唯一的接口。当SQL语句执行完毕时,其对应的work area profile就会被删除。而在SQL语句执行期间,为了反映SQL语句当前已经消耗的内存以及是否被交换到临时表空间了等状态信息,oracle会不断更新其对应的work area profile。所以说,SQL语句的work area profile是有生命周期的,始终能够体现其对应SQL语句的工作区状态。因此,我们可以说,在任何时间点,所有当前活动的work area profile就能够基本体现当前所有session对PGA内存的需要以及当前正在使用的PGA内存。通过查询视图v$sql_workarea_active,可以显示所有当前活动的work area profile的相关信息。
现在,我们需要引入另外一个后台守护进程(background daemon),叫做全局内存管理器(global memory manager)。这个进程每隔3秒会启动一次。每次启动时,都会根据当前所有活动的work area profile的数量以及其他相关信息计算出这个时候的SQL工作区的“内存限度(memory bound)”,也就是每个工作区最大尽量不能超过多大(不过,注意,严格说来应该是尽量不超过。实际上这个最大值是可以被超过的,后面会用个实例来说明)。然后立即发布这个“内存限度”。
最后,本地内存管理器关闭“反馈循环”,并根据当前的“内存限度”以及当前work area profile,从而计算出当前SQL工作区应该具有的内存大小,并为进程分配该大小的内存以执行SQL语句,这个内存的大小尺寸就叫做“期望尺寸(expect size)”,可以从v$sql_workarea_active的expected_size列看到“期望尺寸”的大小。同时,这个“期望尺寸”会定时更新,并据此对SQL工作区进行调整。
Oracle内部对这个“期望尺寸”的大小有如下规则的限制:
“期望尺寸”不能小于最低的内存需求。
“期望尺寸”不能大于optimal尺寸。
如果“内存限度”介于最低的内存需求和optimal尺寸之间,则使用“内存限度”作为“期望尺寸”的大小,但是排序操作除外。因为排序操作算法的限制,对于分配的内存在optimal尺寸和onepass尺寸之间时,排序操作不会随着内存的增加而更快完成,除非能够为排序操作分配optimal尺寸。所以,如果排序操作的“内存限度”介于onepass尺寸和optimal尺寸之间的话,“期望尺寸”取onepass尺寸。
如果SQL以并行方式运行,则“期望尺寸”为上面三个规则算出的值乘以并行度。
非并行模式下,按照通常的说法是“期望尺寸”不能超过min(5%*pga_aggregate_target,100MB)。但实际上,这是在不修改_pga_max_size和_smm_max_size这两个隐藏参数的前提下,可以简单的这么认为。严格说来,应该是不能超过min(5%*pga_aggregate_target,50%*_pga_max_size,_smm_max_size)。对于并行的情况,就更加复杂,可以简单认为不超过30%*pga_aggregate_target。
下面,我们举例(如下图所示)来说明全局内存管理器是如何计算并应用“内存限度”的。比如,
当前系统中有6个活动的work area profile。WP1所需要的onepass内存为7MB,而optimal内存为27MB。WP3是一个并行度为2的hash-join,它需要11MB的onepass内存,以及67MB的optimal的内存。假设pga_aggregate_target设置为133MB,则可以简单的认为全局内存管理器直接将133除以6,也就是大约20MB作为“内存限度”的值。于是该“内存限度”限制了分配给WP1的工作区只能为7MB,也就是onepass的大小,因为WP1是一个排序操作,如果给它分配20MB也不能使它在以optimal的方式完成。而对于20MB的“内存限度”,WP3可以分到40MB的工作区,因为WP3的并行度为2,所以可以分配20MB×2的大小的工作区。
2.3如何设置新数据库的PGA值
我们一旦设置了pga_aggregate_target以后,所有的*_area_size就将被忽略。那么,我们该如何来设置该参数的值呢?这依赖于数据库的用途,如果数据库为OLTP(联机事务处理)应用的,则其应用一般都是小的短的进程,所需要的PGA也相应较少,所以该值该值通常为总共分配给oracle实例的20%,另外的80%则给了SGA;如果数据库为OLAP(DSS)(数据仓库或决策分析)应用的,则其应用一般都是很大的,运行时间很长的进程,因此需要的PGA就多。所以通常为PGA分配50%的内存。而如果数据库为混合类型的,则情况比较复杂,一般会先分配40%的初始值,而后随着数据库的应用,而不断对PGA进行监控,并进行相应的调整。
比如,对于8GB物理内存的数据库服务器来说,按照oracle推荐的,分配给oracle实例的内存为物理内存的80%。那么对于OLTP应用来说,pga_aggregate_target的值大约就是1310MB ((8192 MB× 80%)×20%)。而对于OLAP来说,则该值大约就是3276MB (8192MB×80%)×50%)。
当然,这里所说的都是对于一个新的数据库来说,初始设置的值。这些值并不一定正确,可能设置过大,也可能设置过小。必须随着系统的不断运行,DBA需要不断监控,从而对其进行调整。
文章转载自网管之家:http://www.bitscn.com/pdb/oracle/200611/82339_3.html
本文详细介绍了Oracle数据库中的程序全局区(PGA),包括其概念、组成结构、内存分配原则,以及PGA自动管理机制的深入研究。通过具体示例阐述了如何合理设置PGA参数以优化数据库性能。

389

被折叠的 条评论
为什么被折叠?



