从Oracle9i Release 1起,又引入了一种新的方法来管理PGA内存,即自动PGA内存管理。这种方法中不再使用SORT_AREA_SIZE、BITMAP_MERGE_AREA_SIZE和HASH_AREA_SIZE这些参数。引入自动PGA内存管理是为了解决以下问题:
q 易用性:很多人并不清楚如何设置适当的*_AREA_SIZE参数。另外这些参数具体如何工作,内存究竟如何分配,这些问题都很让人困惑。
q 手动分配是一种“以一概全”的方法:一般地,随着在一个数据库上运行类似应用的用户数的增加,排序/散列所用的内存量也会线性增长。如果有10个并发用户,排序区大小为1 MB,这就会使用10 MB的内存,100个并发用户可能使用100 MB,1 000个并发用户则可能使用1 000 MB,依此类推。除非DBA一直坐在控制台前不断地调整排序/散列区大小设置,否则每个人每天可能都会使用同样的设置值。考虑一下前面的例子,你自己可以清楚地看到, 随着允许使用的RAM量的增加,对临时空间执行的物理I/O在减少。如果你自己运行这个例子,肯定会注意到,随着排序可用RAM的增加,响应时间会减少。手动分配会把排序所用的内存量固定为某个常量值,而不论实际有多少内存。利用自动内存管理的话,只有当内存真正可用时我们才会使用;自动内存管理会根据工作负载动态地调整实际使用的内存量。
q 内存控制:根据上一条,手动分配很难保证Oracle实例“合法”地使用内存,甚至不可能保证。你不能控制实例要用的内存量,因为你根本无从控制会发生多少并发的排序/散列。很有可能要使用的实际内存(真正的物理空闲内存)过多,而机器上并没有这么多可用内存。
下面来看自动PGA内存管理。首先简单地建立SGA并确定其大小。SGA是一段大小固定的内存,所以你可以准确地看到它有多大,这将是SGA的总大小(除非你改变了这个大小)。得到了SGA的大小后,再告诉Oracle:“你就要在这么多内存中分配所有工件区,所谓工作区(work
area)只是排序区和散列区的另一种通用说法。”现在,理论上讲,如果一台机器有2 GB的物理内存,可以分配768 MB内存给SGA,768 MB内存分配给PGA,余下的512 MB内存留给操作系统和其他进程。我提到了“理论上”,这是因为实际情况不会毫厘不差,但是会很接近。为什么会这样呢?在做进一步的解释之前,先来看一下如何建立和打开自动PGA内存管理。
建立自动PGA内存管理时,需要为两个实例初始化参数确定适当的值,这两个参数是:
q WORKAREA_SIZE_POLICY:这个参数可以设置为MANUAL或AUTO,如果是MANUAL,会使用排序区和散列区大小参数来控制分配的内存量;如果是AUTO,分配的内存量会根据数据库中的当前工作负载而变化。默认值是AUTO,这也是推荐的设置。
q PGA_AGGREGATE_TARGET:这个参数会控制实例为完成数据排序/散列的所有工作区(即排序区和散列区)总共应分配多少内存。在不同的版本中,这个参数的默认值有所不同,可以用多种工具来设置,如DBCA。一般来讲,如果使用自动PGA内存管理,就应该显式地设置这个参数。
所以,假设WORKAREA_SIZE_POLICY设置为AUTO,PGA_AGGREGATE_TARGET有一个非0值,就会使用这种新引入的自动PGA内存管理。可以在会话中通过ALTER SESSION命令“打开”自动PGA内存管理,也可以在系统级通过ALTER SYSTEM命令打开。
注意 要记住前面的警告,在Oracle9i中,共享服务器连接不会使用自动PGA内存管理;而是使用SORT_AREA_SIZE和HASH_AREA_SIZE参数来确定为各个操作分配多少RAM。在Oracle 10g 及以上版本中,无论哪种连接(专用服务器连接或共享服务器连接)都能使用自动PGA内存管理。对于Oracle9i,使用共享服务器连接时,要适当地设置SORT_AREA_SIZE和HASH_AREA_SIZE参数,这很重要。
所以,自动PGA内存管理的总目标就是尽可能充分地使用RAM,而且不会超出可用的RAM。倘若采用手动内存管理,这个目标几乎无法实现。如果将SORT_AREA_SIZE设置为10 MB,一个用户完成一个排序操作时,该用户会用掉排序工作区的10 MB内存。如果100个用户执行同样的操作,就会用掉1 000 MB的内存。如果你只有500 MB的空闲内存,那么无论对于单独的1个用户还是对于100个用户,这种设置都是不合适的。对于单独一个完成排序的用户来说,他本来可以使用更多的内存(而不只是10 MB),而对于100个想同时完成排序的用户来说,应该少用一些内存才行(应该少于10 MB)。自动PGA内存管理就是要解决这种问题。如果工作负载小,随着系统上负载的增加,会最大限度地使用内存;随着更多的用户完成排序或散列操作,分配给他们的内存量会减少,这样就能达到我们的目标,一方面使用所有可用的RAM,另一方面不要超额使用物理上不存在的内存。
1. 确定如何分配内存
有几个问题经常被问到:“内存是怎么分配的?”以及“我的会话使用了多少RAM?”这些问题都很难回答,原因只有一个,文档中没有说明采用自动模式时分配内存的算法,而且在不同版本中这个算法还可能(而且将会)改变。只要技术以A开头(表示自动,automatic),你就会丧失一定的控制权,而由底层算法确定做什么以及如何进行控制。
我们可以根据MetaLink 147806.1中的一些信息来做一些观察:
q
PGA_AGGREGATE_TARGET是一个上限目标,而不是启动数据库时预分配的内存大小。可以把PGA_AGGREGATE_TARGET设置为一个超大的值(远远大于服务器上实际可用的物理内存量),你会看到,并不会因此分配很大的内存。
q 串行(非并行查询)会话会使用PGA_AGGREGATE_TARGET中的很少一部分,大约5%或者更少。所以,如果把PGA_AGGREGATE_TARGET设置为100 MB,可能每个工作区(例如,排序或散列工作区)只会使用大约不到5 MB。你的会话中可能为多个查询分配有多个工作区,或者一个查询中就有多个排序/散列操作,但是不论怎样,每个工作区只会用PGA_AGGREGATE_TARGET中不到5%的内存。
q 随着服务器上工作负载的增加(可能有更多的并发查询和更多的并发用户),分配给各个工作区的PGA内存量会减少。数据库会努力保证所有PGA分配的总和不超过PGA_AGGREGATE_TARGET设置的阈值。这就像有一位DBA整天坐在控制台前,不断地根据数据库中完成的工作量来设置SORT_AREA_SIZE和HASH_AREA_SIZE参数。稍后会通过一个测试来观察这种行为。
q 一个并行查询最多可以使用PGA_AGGREGATE_TARGET的30%,每个并行进程会在这30%中得到自己的那一份。也就是说,每个并行进程能使用的内存量大约是0.3*PGA_ AGGREGATE_TARGET / (并行进程数)。
那么,怎么观察分配给会话的不同工作区的大小呢?在介绍手动内存管理那一节中,我们介绍过一种技术,现在可以采用同样的技术来观察会话所用的内存以及对临时空间执行的I/O。以下测试在Red Hat Advanced Server 3.0 Linux主机上完成,使用了Oracle 10.1.0.3和专用服务器连接。这是一个双CPU的Dell PowerEdge,支持超线程(hyperthreading),所以就好像有4个CPU一样。这里又使用了reset_stat.sql,并对前面的watch_stat.sql稍做修改,不仅要得到一个会话的会话统计信息,还将得到实例总的统计信息。这个稍做修改的watch_stat.sql脚本通过MERGE语句来得到这些信息:
![]()
![]()
![]()

除了单个会话的统计信息外,我只增加了UNION ALL部分,通过将所有会话的统计结果累加,从而得到总的PGA/UGA使用情况和排序写次数。然后在这个会话中运行以下SQL*Plus脚本。在此之前已经创建了BIG_TABLE表,而且填入了50 000行记录。我删除了这个表的主键,这样余下的只是表本身(从而确保肯定会执行一个排序过程):
![]()
注意 BIG_TABLE表创建为ALL_OBJECTS的一个副本,并增加了主键,行数由你来定。big_table.sql 脚本在本书开头的“配置环境”一节中介绍过。
下面,对一个数据库运行这个小查询脚本,该数据库的PGA_AGGREGATE_TARGET设置为256 MB,这说明我希望Oracle使用最多约256 MB的PGA内存来完成排序。我还建立了另一个脚本,可以在其他会话中运行,它会在机器上生成很大的排序负载。这个脚本有一个循环,并使用一个内置的包(DBMS_ALERT)查看是否继续处理。如果是,则再一次运行这个大查询,对整个BIG_TABLE表排序。仿真结束后,再由一个会话通知所有排序进程(也就是负载生成器)“停止”并退出。执行排序的脚本如下:
![]()

![]()

以下脚本会让这些进程停止运行:

为了观察对所测量的会话分配的RAM量有什么不同,首先独立地运行SELECT查询,这样就只有一个会话。我得到了与前面相同的6个统计结果,并把这些统计结果连同活动会话数保存在另一个表中。然后,再向系统增加25个会话(也就是说,在25个新会话中运行以上带循环的基准测试脚本)。接下来等待很短时间(1分钟),让系统能针对这个新负载做出调整。然后我创建了一个新会话,用reset_stat.sql得到其统计结果,再运行执行排序的查询,接下来运行 watch_stat.sql得到排序前后的统计结果之差。然后重复地做了这个工作,直至并发用户数达到500。
需要说明,这里实际上在要求数据库实例做它根本做不了的事情。前面已经提到,第一次运行watch_stat.sql时,每个Oracle连接在完成排序之前会使用大约0.5 MB的RAM。如果有500个并发用户全部登录,单单是他们登录所用的内存就已经非常接近所设置的PGA_AGGREGATE_TARGET(PGA_AGGREGATE_TARGET设置为256 MB),更不用说具体做工作了!由此再一次表明,PGA_AGGREGATE_TARGET只是一个目标,而不是明确地指定要分配多少空间。出于很多原因,实际分配的空间还可能超过这个值。
表4-1总结了每次增加大约25个用户时得到的统计结果。
表4-1 随着活动会话数的增加,PGA内存分配行为的变化(PGA_AGGREGATE_TARGET设置为256 MB)
|
活动会话 |
一个会话使用的PGA |
系统使用的PGA |
一个会话的临时写 |
一个会话的临时读 |
|
1 |
7.5 |
2 |
0 |
0 |
|
27 |
7.5 |
189 |
0 |
0 |
|
51 |
4.0 |
330 |
728 |
728 |
|
76 |
4.0 |
341 |
728 |
728 |
|
101 |
3.2 |
266 |
728 |
728 |
|
|
1.5 |
214 |
728 |
728 |
(续)
|
活动会话 |
一个会话使用的PGA |
系统使用的PGA |
一个会话的临时写 |
一个会话的临时读 |
|
151 |
1.7 |
226 |
728 |
728 |
|
|
1.4 |
213 |
728 |
728 |
|
201 |
1.3 |
218 |
728 |
728 |
|
226 |
1.3 |
211 |
728 |
728 |
|
251 |
1.3 |
237 |
728 |
728 |
|
276 |
1.3 |
251 |
728 |
728 |
|
301 |
1.3 |
281 |
728 |
728 |
|
326 |
1.3 |
302 |
728 |
728 |
|
351 |
1.3 |
324 |
728 |
728 |
|
376 |
1.3 |
350 |
728 |
728 |
|
402 |
1.3 |
367 |
728 |
728 |
|
426 |
1.3 |
392 |
728 |
728 |
|
452 |
1.3 |
417 |
728 |
728 |
|
476 |
1.3 |
439 |
728 |
728 |
|
501 |
1.3 |
467 |
728 |
728 |
注意 你可能会奇怪,有1个活动用户时,为什么系统使用的RAM只报告为2 MB。这与我的测量方法有关。这个仿真会对所测会话中的统计结果建立快照。接下来,我会在所测的这个会话中运行上述大查询,然后再次记录该会话统计结果的快照。最后再来测量系统使用了多少PGA。在我测量所用的PGA时,这个会话已经结束,并交回了它用于排序的一些PGA。所以,这里系统使用的PGA只是对测量时系统所用PGA内存的准确度量。
可以看到,如果活动会话不多,排序则完全在内存中执行。活动会话数在1~50之间时,我就可以完全在内存中执行排序。不过,等到有50个用户登录并执行排序时,数据库就会开始控制一次能使用的内存量。所用的PGA量要退回到可接受的限值(256 MB)以内,在此之前需要几分钟的时间来调整,不过最后总是会落回到阈值范围内。分配给会话的PGA内存量从7.5
MB降到4 MB,随后又降到3.2 MB,最后降至1.7~1.3 MB之间(要记住,PGA中有一部分不用于排序,也不用于其他操作,光是登录这个动作就要创建0.5
MB的PGA)。系统使用的总PGA量仍保持在可以接受的限值内,直至用户数达到300~351之间。从这里开始,系统使用的PGA开始有规律地超过PGA_AGGREGATE_TARGET,并延续至测试结束。在这个例子中,我交给数据库实例一个不可能完成的任务,光是支持350个用户(大多数都执行一个PL/SQL,再加上他们都要请求排序),我设定的这个目标(256 MB的RAM)就无法胜任。每个会话只能使用尽可能少的内存,但另一方面又必须分配所需的足够内存,所以这根本就办不到。等我完成这个测试时,500个活动会话已经使用了总共467 MB的PGA内存,大大超出了我所设定的目标(256 MB),但对每个会话来说使用的内存已经够少的了。
不过,再想想在手动内存管理情况下表4-1会是什么样子。假设SORT_AREA_SIZE设置为5 MB。计算很简单:每个会话都能在RAM中执行排序(如果实际RAM用完了,还可以使用虚拟内存),这样每个会话会使用6~7 MB的RAM(与前面只有一个用户而且不在磁盘上排序时使用的内存量相当)。再运行前面的测试,将SORT_AREA_SIZE设置为5 MB,从1个用户开始,每次增加25个用户,得到的结果保持一致,如表4-2所示。
表4-2 随着活动会话数的增加,PGA内存分配行为的变化(手动内存管理,SORT_AREA_SIZE设置为5 MB)
|
活动会话 |
一个会话使用的PGA |
系统使用的PGA |
一个会话的临时写 |
一个会话的临时读 |
|
1 |
6.4 |
5 |
728 |
728 |
|
26 |
6.4 |
137 |
728 |
728 |
|
51 |
6.4 |
283 |
728 |
728 |
|
76 |
6.4 |
391 |
728 |
728 |
|
102 |
6.4 |
574 |
728 |
728 |
|
126 |
6.4 |
674 |
728 |
728 |
|
151 |
6.4 |
758 |
728 |
728 |
|
176 |
6.4 |
987 |
728 |
728 |
|
202 |
6.4 |
995 |
728 |
728 |
|
226 |
6.4 |
1227 |
728 |
728 |
|
251 |
6.4 |
1383 |
728 |
728 |
|
277 |
6.4 |
1475 |
728 |
728 |
|
302 |
6.4 |
1548 |
728 |
728 |
如果我能完成这个测试(这个服务器上有2 GB的实际内存,我的SGA是600 MB;等到用户数达到325时,机器换页和交换开始过于频繁,而无法继续工作),有500个并发用户时,我就要分配大约2 750 MB的RAM!所以,在这个系统上DBA可能不会将SORT_AREA_SIZE设置为5 MB,而是设置为0.5 MB,力图使高峰期的最大PGA使用量在可以忍受的范围内。现在如果并发用户数为500,就要分配大约500 MB的PGA,这可能与采用自动内存管理时所观察到的结果类似,但是即使用户不太多,还是会写临时空间,而不是在内存中执行排序。实际上,如果SORT_AREA_SIZE设置为0.5 MB,再运行以上测试,会观察到表4-3所示的数据。
表4-3 随着活动会话数的增加,PGA内存分配行为的变化(手动内存管理,SORT_AREA_SIZE设置为0.5 MB)
|
活动会话 |
一个会话使用的PGA |
系统使用的PGA |
一个会话的临时写 |
一个会话的临时读 |
|
1 |
1.2 |
1 |
728 |
728 |
|
26 |
1.2 |
29 |


