手动PGA内存管理

有些参数对PGA大小的影响最大,这些参数如下:

SORT_AREA_SIZE:在信息换出到磁盘之前,用于对信息排序的RAM总量

SORT_AREA_RETAINED_SIZE:排序完成后用于保存已排序数据的内存总量。

如果SORT_AREA_SIZE是512 KB,SORT_AREA_RETAINED_SIZE是256 KB,那么服务器进程最初处理查询时会用512 KB的内存对数据排序。等到排序完成时,排序区会“收缩”为256 KB,这256 KB内存中放不下的已排序数据会写出到临时表空间中。

HASH_AREA_SIZE:服务器进程在内存中存储散列表所用的内存量。散列联结时会使用这些散列表结构,通常把一个大集合与另一个集合联结时就会用到这些结构。两个集合中较小的一个会散列到内存中,散列区中放不下的部分都会通过联结键存储在临时表空间中。


Oracle将数据写至磁盘之前,数据排序或散列所用的空间量就由这些参数控制,它们还控制着排序完成后会保留多少内存段。SORT_AREA_SIZE~SORT_AREA_ RETAINED_SIZE这部分内存一般从PGA分配,SORT_AREA_RETAINED_SIZE这部分内存会在UGA中分配。通过查询一些特殊的Oracle V$视图,可以看到PGA和UGA内存的当前使用情况,并监视其大小的变化,这些特殊的V$视图也称为动态性能视图(dynamic performance view)。


例如,下面来运行一个小测试,这里会在一个会话中对大量数据排序,在第二个会话中,我们将监视第一个会话中UGA/PGA内存的使用。为了以一种可预测的方式完成这个工作,我们建立了ALL_OBJECTS表的一个副本,其中有大约45 000行,而且没有任何索引(这样就能知道肯定会发生排序):

scott@ORCL>create table t as
  2  select * from all_objects;

表已创建。

为了消除最初硬解析查询所带来的副作用,我们将运行以下脚本,后面还会在一个新会话中再次运行这个脚本,查看受控环境中对内存使用的影响。我们会依次使用大小为64 KB、1 MB和1 GB的排序区:

scott@ORCL>exec dbms_stats.gather_table_stats(user,'T');
PL/SQL 过程已成功完成。


scott@ORCL>alter session set workarea_size_policy=manual;
scott@ORCL>alter session set sort_area_size=65536;
scott@ORCL>set termout off;
scott@ORCL>select * from t order by 1,2,3,4;


scott@ORCL>set termout on;
scott@ORCL>alter session set sort_area_size=1048576;
会话已更改。
scott@ORCL>set termout off;
scott@ORCL>select * from t order by 1,2,3,4;


scott@ORCL>set termout on;
scott@ORCL>alter session set sort_area_size=1073741820;
会话已更改。
scott@ORCL>set termout off;
scott@ORCL>select * from t order by 1,2,3,4;
scott@ORCL>set termout on;


注销,重新登录

为了确保使用手动内存管理,我们要专门设置,并指定一个很小的排序区大小(64 KB)。另外,还要标识会话ID(SID),以便监视该会话的内存使用情况。

scott@ORCL>alter session set workarea_size_policy=manual;
会话已更改。

scott@ORCL>select sid from v$mystat where rownum=1;
       SID
----------
       7

下面需要在第二个单独的会话中测量第一个会话(SID 7)使用的内存。

准备两个脚本,一个为reset_stat.sql,用于重置一个小表,并将一个SQL*Plus变量设置为SID,这个脚本如下:

drop table sess_stats;
create table sess_stats
( name varchar2(64),
 value number, 
diff number );
variable sid number
exec :sid := &1

执行该脚本:

scott@ORCL>@D:\app\Administrator\product\11.2.0\reset_stat.sql;
表已删除。
表已创建。
输入 1 的值:  7
PL/SQL 过程已成功完成。

另一个脚本是watch_stat.sql,脚本中使用了MERGE SQL语句,这样就能首先插入(INSERT)一个会话的统计值,以后再回过来对其进行更新,而无需单独的INSERT/UPDATE脚本:

merge into sess_stats
using
(
select a.name, b.value
from v$statname a, v$sesstat b
where a.statistic# = b.statistic#
and b.sid = :sid
and (a.name like '%ga %'
or a.name like '%direct temp%')
) curr_stats
on (sess_stats.name = curr_stats.name)
when matched then
update set diff = curr_stats.value - sess_stats.value,
value = curr_stats.value
when not matched then
insert ( name, value, diff )
values
( curr_stats.name, curr_stats.value, null )
/
select *
from sess_stats
order by name;

再执行watch_stat.sql脚本,可以看到会话的PGA和UGA内存统计信息列表,而且列出了对临时空间执行的I/O。在对会话196(也就是使用手动PGA内存管理的会话)做任何工作之前,下面使用以上脚本来看看这个会话当前使用了多少内存,以及对临时空间执行了多少次I/O:

scott@ORCL>@D:\app\Administrator\product\11.2.0\watch_stat.sql;

6 行已合并。


NAME                                                                  VALUE    DIFF
---------------------------------------------------------------- ---------- ----------
physical reads direct temporary tablespace                                0
physical writes direct temporary tablespace                               0
session pga memory                                                   971336
session pga memory max                                               971336
session uga memory                                                   311984
session uga memory max                                               311984

可以看出,开始查询之前,UGA中大约有304KB(311984/1024)的数据,PGA中大约有949 KB的数据.
如果采用专用服务器模式,UGA完全包含在PGA中,在这种情况下,进程或线程就使用949KB的内存
如果使用共享服务器,UGA将从SGA中分配,PGA则在共享服务器中,
从技术上讲,我们使用了304KB的内存。

由于我们还没有设置SORT_AREA_RETAINED_SIZE,所以报告的SORT_AREA_RETAINED_SIZE值将是0,但是排序区实际保留的大小等于SORT_AREA_SIZE

会话一

scott@ORCL>alter session set sort_area_size = 65536;
会话已更改。
scott@ORCL>set termout off;
scott@ORCL>select * from t order by 1,2,3,4;
scott@ORCL>set termout on;

会话二:

scott@ORCL>@D:\app\Administrator\product\11.2.0\watch_stat.sql;
6 行已合并。


NAME                                                                  VALUE    DIFF
---------------------------------------------------------------- ---------- ----
------
physical reads direct temporary tablespace                             5190    5190
physical writes direct temporary tablespace                            5190    5190
session pga memory                                                  1290616    319280
session pga memory max                                              2216520    1245184
session uga memory                                                   376856    64872
session uga memory max                                              1492104    1180120

已选择6行。

session xxx memory值表示我们现在使用了多少内存
session xxx memory max值表示会话处理查询时某个时刻所使用内存的峰值


可以看到,使用的内存增加了,这里对数据做了某种排序。在处理查询期间,UGA临时从304 KB增加到1457 KB(增加了1153 KB),然后再收缩回368kb(376856/1024)。这是因为,为了完成查询和排序,Oracle为会话分配了一个排序区。另外,PGA内存从949KB增加到551 KB,增加了2165KB。另外可以看到,我们对临时空间执行了5190次读和写。

如以上结果所示,完成查询并得到结果集之前,UGA内存又退回到原来的大小(从UGA释放了排序区),PGA也会有某种程度的收缩。


下面再来完成这个操作,这次SORT_AREA_SIZE增加到1 MB。注销所监视的会话,再登录:

scott@ORCL>alter session set sort_area_size = 1048576;
会话已更改。
scott@ORCL>set termout off;
scott@ORCL>select * from t order by 1,2,3,4;
scott@ORCL>set termout on;

下面再在另一个会话中测量内存的使用情况:

scott@ORCL>@D:\app\Administrator\product\11.2.0\reset_stat.sql;
表已删除。
表已创建。
输入 1 的值:  7
PL/SQL 过程已成功完成。

scott@ORCL>@D:\app\Administrator\product\11.2.0\watch_stat.sql;
6 行已合并。


NAME                                                                  VALUE    DIFF
---------------------------------------------------------------- ---------- ----
------
physical reads direct temporary tablespace                                0
physical writes direct temporary tablespace                               0
session pga memory                                                   766328
session pga memory max                                             11391560
session uga memory                                                   377472
session uga memory max                                             10593600

已选择6行。
可以看到,这一次处理查询期间,PGA大幅增长。它临时增加了大约8960KB,但是进行数据排序所必须执行的物理I/O次数则显著下降( 使用更多的内存,就会减少与磁盘的交换)。而且,我们还可以避免一种多趟排序(multipass sort),如果有太多很小的排序数据集合要合并(或归并),Oracle最后就要多次将数据写至临时空间,这种情况下就会发生多趟排序。现在,再来看一个极端情况:
scott@ORCL>alter session set sort_area_size = 1073741820;
会话已更改。
scott@ORCL>set termout off;
scott@ORCL>select * from t order by 1,2,3,4;
scott@ORCL>set termout on;

从另一个会话进行测量,可以看到迄今为止所使用的内存:

scott@ORCL>@D:\app\Administrator\product\11.2.0\watch_stat.sql;

6 行已合并。


NAME                                                                  VALUE
  DIFF
---------------------------------------------------------------- ---------- ----
------
physical reads direct temporary tablespace                                0
     0
physical writes direct temporary tablespace                               0
     0
session pga memory                                                   766328
     0
session pga memory max                                             11391560
     0
session uga memory                                                   377472
     0
session uga memory max                                             10593600
     0

已选择6行。

尽管允许SORT_AREA_SIZE有1 GB,但实际上只用了大约10.1MB。

这说明SORT_AREA_SIZE设置只是一个上界,而不是默认的分配大小

这里也做了排序,但是这一次完全在内存中进行;而没有使用磁盘上的临时空间,从物理I/O次数为0可以看出这一点。

增加允许的排序区大小并完成大规模排序时,会话使用的内存量会增加。

在使用*_AREA_SIZE参数时,记住以下几点:

猜你喜欢

转载自blog.csdn.net/a0001aa/article/details/79773171