《Oracle编程艺术》第四章-内存管理,4.1.1手动PGA内存管理

采用手动PGA内存管理,除了你的会话为PL/SQL中的表和其他变量分配的内存以外,以下参数对PGA大小的影响最大。

  1. SORT_AREA_SIZE:在排序信息被交换到磁盘之前,所使用的内存总量(磁盘是指用户指定的磁盘上的临时表空间
  2. SORT_AGRE_RETAINED_SIZE:排序完成后用于保存已排序数据的内存总量。就是说,如果SORT_AREA_SIZE是512KB且SORE_AREA_RETAINED_SIZE是256KB,那么服务器进程最初处理查询时会使用512KB的内存对数据排序。等待排序完成时,排序区会"收缩"为256KB,这256KB内存中放不下的已排序数据会写出到临时表空间中。(所以会有磁盘I/O)
  3. HASH_AREA_SIZE:服务器进程在内存中存储散列表所用的内存量。通常在做一个大数据集与另一个数据集的连接时,会进行散列连接并使用到这部分内存。两个数据集中较小的一个会被散列到内存中,内存中的散列区中放不下的部分会通过连接键(join key)存储在临时表空间中。

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

(临时表空间:<https://www.cnblogs.com/zhangyingai/p/7082594.html>  或者Oracle官方文档 https://docs.oracle.com/

如下为具体的实践内容:

运行一个小测试,这里会在一个会话中对大量数据进行排序,而我们将会在第二个会话中监视第一个会话里UGA/PGA内存的使用情况。

 

为了以一种可预测的方式进行这个工作,我们建立了ALL_OBJECTS表的一个副本,其中有大约72000行,而且没有任何索引(这样,在这个表上使用ORDER BY时,就知道肯定会发生排序)

 

创建测试表:SQL>create table t as select * from all_objects;

收集信息:SQL>exec dbms_stats.gather_table_stats(user,'T');

为了消除最初硬解析查询带来的影响,我们将运行以下脚本三次。依次使用大小为64KB,1MB,1GB的排序区。

具体脚本内容如下:

 

名词介绍--硬解析:数据库中处理SQL时,首先必须"解析"SQL语句。有两种类型的解析,第一种是硬解析(hard parse),发生在某个查询第一次在数据库实例中被解析时,其中包括查询计划的生成和优化。第二种是软解析(soft parse),它会在此跳过硬解析的许多步骤并直接使用这些步骤的结果。由于前面的查询完成了硬解析,后面在查询时就可以使用软解析,不必再考虑硬解析的开销。

 

建议注销刚才的SQL*Plus会话,再重新登录,这样能得到一个和之前一样没有做过任何操作的环境。下面需要在第二个独立的会话中查看运行ORDER BY的会话所使用的内存。

如果使用同一个会话来做ORDER BY 查询并查看内存使用情况,那查看操作本身可能会影响到结果。在第二个会话中要使用另一个脚本(实际上这是一对脚本,运行run_query.sql脚本时将会通知你去运行它们)。其中一个叫reset_stat.sql,用于重置一个小表,并将一个SQL*Plus变量设置为SID(session ID),该脚本如下:

(如果使用的表名字已经存在,则换个表名)

create table sess_stats(name varchar2(64),value number,diff number);

variable sid number

exec :sid := &1

另一个脚本是watch_stat.sql,使用了MERGE SQL语句,这样就能先插入一个会话的统计值,然后回过头来再用它进行更新,而无需分别写INSERT和UPDATE脚本。

 

在第一个会话中运行run_query.sql结果:

 

可以看到,第一个会话的SID是23,而且我们已经将PGA内存管理设置为手动管理,另外sort_area_size设置为65536(64KB)。现在这个脚本告诉我们要在另一个会话中运行另外两个脚本,我们照办,运行结果如下

分析结果如下:

到目前为止,UGA中大约有334KB的数据,PGA中大约有876KB的数据。第一个问题是:"我们在PGA和UGA上共使用了多少内存?" 是使用了334KB+876KB的内存吗?

如果是使用专用服务器连接,那么UGA被包含在PGA里,进程或线程就是使用了876KB的内存。

如果是使用共享服务器连接,UGA将从SGA中分配,PGA是从系统内存中分配并专属于共享服务器进程。查询完成后,该共享服务器的进程可能会被其他会话请求所用,这时这个PGA不再是"我们的"了,所以从技术上讲,我们只能确定使用了334KB的UGA内存(如果我们的查询正在进行,那么可以确定PGA和UGA总共使用了1210KB内存)。

测试时使用的是专用服务器连接,否则无法保证统计的准确性。所以PGA和UGA总共使用了876KB的内存。

 

在会话23中运行第一个大查询,这个会话采用专用服务器模式,并使用手动PGA内存管理。回到第一个会话,点击回车键启动查询。完成后,回到第二个会话,执行watch_stat.sql文件,得到如下结果

这一次session xxx memory和session xxx memory max值并不一致。

session xxx memory值表示当前我们使用了多少内存。session xxx memory max值表示会话处理查询过程中内存使用的峰值。可以看到,使用的内存增加了,已经对数据做了某种排序。在处理查询期间,UGA的使用峰值从334KB增加到718KB。这是因为,为了完成查询和排序,Oracle为会话分配了一个排序区。另外PGA内存从876KB增加到1196KB。同时可以看到,我们对临时空间进行了3000次的读和写(根据表数据及脚本定义计算得知)(因为排序操作的数据超出了SORE_AREA_SIZE---64KB的容量,所以要写到临时表空间)

如上图所示,完成查询并得到结果之后,PGA有某种程度的收缩(在Oracle Database 8i和以前的版本中不会出现PGA的收缩,这是Oracle Database 9i及更高版本中的新特性)。

 

下面将SORT_AREA_SIZE的大小提高到1MB,重新做一次实验。注意注销正在被监视的查询会话。

下面只说结果:

UGA内存用量大幅增加。峰值达到了1756KB(比SORT_AREA_SIZE(即1MB)大一点),但是进行数据排序所用的物理I/O次数则显著下降(使用内存越多,交换到磁盘越少)。而且,我们可能也已经避免了一种叫做多趟排序的情况:有很多很小的排序数据集,Oracle最终需要分多次将数据写至临时表空间并进行合并,这就是多趟排序。

 

最后,在来看一个极端的情况,将SORT_AREA_SIZE的大小提高到1GB。重新做一次实验。

结果:

发现UGA峰值只有10MB。说明SORT_AREA_SIZE设置只是一个上界,而不是默认的分配大小。同时要注意,这里也做了排序,但是这一次完全是在内存中进行的;而没有使用磁盘上的临时表空间,这一点从屋里I/O次数为0是可以看出来的。

 

注意:以上结果因操作系统、数据库配置等等因素可能出现差异。但规律大致是一样的(上面的代码都是完整、有效的。可以直接用)。

 

(为什么Oracle Database 8i上PGA不会收缩呢?

在8i中,PGA被作为堆来管理,并通过malloc()分配内存来创建。在9i及更高版本中,则会根据实际需要调用操作系统特定的方法来分配和释放工作区的内存)

 

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

1.这些参数控制着一个排序、散列或位图合并操作所用的最大内存量

2.一个查询可能包含多个操作,这些操作可能都要使用这部分内存,并且会创建多个排序/散列区。同时还要记住,你也可以同时打开多个游标,每个游标都有自己的SORT_AREA_RETANINED需求。所以,如果把排序区大小设置为10MB,在会话中实际上可能用到10MB、100MB、1000MB或更多内存。设置这些参数并非是对整个会话的限制,它们只是对会话中的一个操作进行限制。在会话中,一个查询可以有多个排序,或者多个查询需要用到一个排序。(这段话没明白啥意思?)

3.这些内存区是根据需要来分配的。如果我们将排序区大小设置为1GB,并不是说你实际分配了1GB的内存给排序区,而是说允许最多分配1GB的内存。

发布了57 篇原创文章 · 获赞 20 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/shafatutu/article/details/95011479