Oracle从新手到高手
上QQ阅读APP看书,第一时间看更新

2.4 实例的内存结构

内存结构是Oracle数据库体系结构中最重要的部分之一,内存也是影响数据库性能的主要因素。在Oracle数据库中,服务器内存的大小将直接影响数据库的运行速度,特别是多个用户连接数据库时,服务器必须有足够的内存支持,否则有的用户可能连接不到服务器,或查询速度明显下降。

实例的内存结构从总体上看可以分为两部分:SGA区(系统全局区,System Global Area)和PGA区(程序全局区,Program Global Area)。SGA区位于系统的共享内存段中,因此SGA区中的数据可以被所有的服务和后台进程共享;PGA区中保存的是某个服务进程私有的数据和控制信息,它是非共享内存。Oracle中每个服务进程都拥有自己的PGA区。

2.4.1 系统全局区

系统全局区SGA是由一组内存结构组成的,它是由所有用户进程共享的一块内存区域。如果多个用户连接同一个数据库实例,则实例的SGA区中的数据可被多个用户共享。在数据库实例启动时,SGA的内存被自动分配;当数据库实例关闭时,SGA被回收。SGA区中主要包含这些内存结构:数据缓存区、共享池、重做日志缓存、大型池和Java池等结构。需要注意,Oracle 8i使用静态内存管理,即SGA内存区是预先在参数中配置好的,数据库启动时就按这些配置来进行内在分配,Oracle 10g和11g引入了动态内存管理,即在数据库运行过程中,内存大小可以在线修改与自动配置。

1. 数据缓冲区

数据缓冲区(Database Buffer Cache)用于存放最近访问的数据块信息。当用户向数据库请求数据时,如果所需的数据已经位于数据缓冲区,则Oracle将直接从数据缓冲区提取数据并返回给用户,而不必再从数据文件中读取数据。

数据缓冲区为所有用户所共享。当用户第一次执行查询或修改数据操作时,后台将所需的数据从数据文件中读取出来,并装入数据缓冲区。当其他用户或该用户再访问相同的数据时,Oracle就不必再从数据文件中读取数据,而可以直接将数据缓存区中的数据返回给用户。由于访问内存的速度要比访问硬盘快许多倍,这样可以极大地提高数据库对用户请示的响应速度。

数据缓冲区由许多大小相同的缓存块组成,这些缓存块的大小与数据块的大小相同。根据缓存块是否被使用,可以将数据缓存区中的缓存块分为如下三类。

※ 脏缓存块:脏缓存块中保存的数据为已经被修改过的数据,这些数据需要重新被写入数据文件。当一条SQL语句对某些缓存块中的数据进行修改后,这些缓存块将被标记为“脏”,然后等待后台进程DBWR写回数据文件,永久地保留更改结果。

※ 空闲缓存块:空闲缓存块中不包含任何数据,它们在等待后台进程或服务器进程向其中写入数据。当Oracle从硬盘的数据文件中读取数据后,将会寻找空闲缓存块来容纳这些数据。

※ 命中缓存块:命中缓存块是那些正在被用户访问的缓存块,这些缓存块将被保留在数据缓冲区中,不会被移出内存。

Oracle通过两个列表来管理数据缓冲区中的缓存块,这两个列表分别是写入列表(DIRTY)和最近最少使用列表(LRU)。这两个列表的作用如下。

※ 最近最少使用列表(LRU列表):LRU列表包含所有的空闲缓存块、命中缓存块以及脏缓存块。LRU列表使用LRU算法,将数据缓冲中那些最近一段时间内访问次数最少的缓存块移出缓冲区,这样可以保证最频繁使用的块被保留在内存中,而将不必要的数据移出缓冲区。

※ 写入列表(脏缓存块):脏缓存块列表包含那些已经被修改,并且需要重新写入数据文件的缓存块。

可以将LRU列表看作一个队列,那些最近最频繁使用的缓存块位于队列的头部,而最近最少使用的缓存块被放置在队列的尾部。当某个进程访问某个缓存块时,这个缓存块将被移动到LRU列表的头部。被访问的缓存块不断地移动到头部,LRU列表中的其他不经常被访问的脏缓存块将逐渐向LRU尾部移动。

当某个用户进程需要访问某些数据块时,Oracle首先在数据缓冲区中查找,如果该进程所需要的数据块已经位于数据缓冲区中,Oracle将直接从内存中读取数据并返回给用户,这种情况称为“缓存命中”。反之,如果在数据缓冲区中找不到所需的数据块,Oracle必须先从数据文件中将所需的数据块复制到缓存中,然后再从缓存中读取它并返回给用户,这种情况称为“缓存失败”。

如果发生缓存失败,则在将数据文件中的数据复制到缓冲区之前,必须先找到空闲缓存块以容纳新的数据块。Oracle将从LRU列表中查找空闲缓存块,如果没有空闲块,则将LRU列表中的脏缓存块移入脏缓存块列表中。当脏缓存块列表超过一定长度时,再由后台进程DBWR将脏缓存块中的数据写入磁盘数据文件,重新刷新数据缓冲区,最后再将磁盘数据文件中的数据读出并存入数据缓冲区。

在Oracle8i以前的版本中,数据缓存的大小由DB_BLOCK_SIZE和DB_BLOCK_BUFFERS两个参数决定,其中DB_BLOCK_SIZE参数用于决定数据块的大小;而DB_BLOCK_BUFFERS参数用于设置数据缓冲区所包含的缓存块的数量。这样,数据缓冲区的大小就为这两个参数的乘积。在Oracle 9i后,数据缓存的大小可以直接由DB_CACHE_SIZE参数指定。

数据缓冲区对数据库的存取速度有直接的影响。如果数据缓冲区设置得过小,则缓存失败的概率将增大,这将影响数据库的响应速度,特别是用户较多时尤为明显。因此,如果能够使用更大的数据库缓存,就可以明显提高数据库的访问性能。但是系统的物理内存是有限的,如果将有限的物理内存全部分配给数据库缓存,则会影响操作系统和数据库其他方面的性能。因此,在调整数据缓冲区时,需要权衡利弊,设置适当的数据缓冲区大小。

2. 共享池

共享池是SGA区中的一系列内存结构,用于缓存SQL或PL/SQL语句、数据字典、资源锁以及其他控制结构相关的数据。在共享池中主要包括:库缓存、数据字典缓存,以及用于存储并行操作信息和控制结构的缓存。

在Oracle 11g中,可以通过使用ALTER SYSTEM命令修改SHARED_POOL_SIZE参数,动态改变共享池的尺寸。例如:

      SQL> alter system set shared_pool_size=10m;
      系统已更改。

下面对共享池的主体库缓存和数据字典缓存区进行介绍。

库缓存

库缓存区用于存放最近执行过的SQL语句和PL/SQL程序代码信息,以提高SQL语句或PL/SQL程序的执行效率。当提交一条SQL语句时,Oracle首先在共享池的库缓冲区内进行搜索,查看相同的SQL语句是否已经被解析并执行。如果存在,Oracle将利用缓冲区中的SQL语法分析结果和执行计划来执行该语句,而不必重新解析。使用缓冲区中的解析代码,可以明显提高SQL语句和PL/SQL程序的执行速度。

库缓存中主要包括共享SQL工作区和私有SQL工作区两个结构。每条被缓存的SQL语句都被分成两部分,分别存放在共享SQL工作区和私有SQL工作区中。共享SQL工作区中存放着SQL语句的语法分析结果和执行计划,如果以后其他用户执行类似的SQL语句,则可以利用共享SQL工作区中已缓存的信息,这样可以提高语句执行的速度。Oracle在执行一条新的SQL语句时,将为它在共享SQL工作区中分配空间。分配空间的大小取决于SQL语句的复杂程度,如果共享区中已经没有空闲空间,Oracle则会利用LRU算法移除最近最少使用的SQL语句,以释放更多的空闲空间。

私有SQL工作区中存放着SQL语句中的绑定变量、环境和会话参数等信息,这些信息是执行该语句的用户私有信息,其他用户即使执行相同的SQL语句,也不能使用这些信息。

数据字典缓存

在数据库的运行过程中,Oracle会频繁对数据字典和视图进行访问。为了提高访问效率,Oracle在共享池的数据字典缓冲区中保存了最常用的数据字典信息,如数据库用户的账户信息、数据库的结构信息等。

3. 重做日志缓存区

重做日志缓存区是位于SGA区中的一个缓存区,用于缓存在对数据进行修改的操作过程中生成的日志信息。当重做日志缓冲区中的日志信息达到一定的数量时,由日志写入进程LGWR就会将日志信息写入重做日志文件。

重做日志缓存区是一个循环缓存区,在使用时从顶端向底端写入数据,当到达日志缓冲区的底端时,再返回到缓冲区的起始点循环写入。

日志缓冲区的大小由LOG_BUFFER参数指定,该参数也可以在数据库运行过程中动态修改。相对于数据缓存区而言,重做日志缓存区的大小对数据库性能的影响较小,通常较大的重做日志缓存区能减少重做日志文件的I/O次数,比较适合长时间运行的、会产生大量记录的操作。

4. 大型池

大型池是SGA区中可选的一个内存结构。数据库管理员可以根据实际的需要决定是否在SGA区中创建大型池。在执行某些特定类型的操作时,可能会需要在内存中使用大量的缓存,这些操作包括:

※ 数据库的备份或恢复操作。

※ 执行具有大量排序操作的SQL语句。

※ 执行并行化的数据库操作。

如果没有在SGA区中创建大型池,则上述操作所需的缓存空间将在共享池或PGA中分配。由于这些操作将占用大量的缓存空间,因此会影响共享池或PGA的使用效率。

大型池的大小通过LAGE_POOL_SIZE参数定义,在Oracle 9i之前,为了改变大型池的尺寸,必须修改参数文件并重新启动数据库。从Oracle 9i开始,数据库管理员可以使用ALTER SYSTEM命令动态改变大型池的尺寸。例如:

5. Java池

Java池(Java pool)是Oracle 8.1.5版本中增加的,目的是支持在数据库中运行Java程序。如果使用Java编写一个存储过程,Oracle会在处理代码时使用Java池的内存。JAVA_POOL_ SIZE参数用于确定为所有Java代码和数据分配的Java池的内存量。

2.4.2 程序全局区

程序全局区(PGA)是保存特定服务进程的数据和控制信息的内存结构,这个内存结构是非共享的,只有服务进程本身才能够访问它自己的PGA区。每个服务进程都有它自己的PGA区,各个服务进程PGA区的总和即为实例的PGA区的大小。

PGA区是每个服务进程所拥有的一块非共享内存区域,它不属于实例的内存结构,而应当看作进程结构的一部分。PGA的内容与结构和数据库的连接模式有关,在专用服务器模式和共享服务器模式下,PGA有着不同的结构和内容。一般情况下,PGA区都由私有SQL工作区和会话内存区组成。

1. 私有SQL工作区

当一个用户连接数据库时,将在实例中创建一个会话。SQL工作区中包含绑定变量,以及SQL语句运行时的内存结构等信息。多条相同的SQL语句只会建立一个共享SQL工作区,但是可以对应于多个私有SQL工作区;如果多个用户执行一条相同的SQL语句,则每个用户都会为自己的会话创建一个私有SQL工作区,而使用同一个共享SQL工作区来保存共享信息。因此,多个私有SQL工作区可能关联到同一个共享SQL工作区。将一个私有SQL工作区与对应的共享SQL工作区合并在一起,就可以获得一条SQL语句的完整缓存数据。

提示:

共享SQL工作区位于SGA区的共享池中。

每个会话的私用SQL工作区可以分为两部分—静态区和动态区。静态区中的内容在会话过程中会保持不变,直到会话结束时静态区才被释放;而动态区中的内容在会话过程中是不断变化的,一旦SQL语句执行完毕,动态区就会被释放。

通常Oracle在开始执行SQL语句时创建动态区。对于INSERT、UPDATE和DELETE语句来说,一旦语句执行完毕,动态区将被立即释放。而对于SELECT语句而言,Oracle将等到所有的查询结果都被返回才会释放动态区。

在执行比较复杂的查询时,经常需要在PGA中创建一个比较大的动态区,用来专门执行如下必须在内存中进行的操作。

※ 排序:包括使用ORDER BY、GROUP BY等子句的查询语句。

※ 连接:包括各种连接查询语句。

※ 集合运算:包括使用union、interset、minus等运算符的查询语句。

在手动管理PGA区的情况下,数据库管理员需要设置sort_area_size、hash_area_size、create_bitmap_area_size等初始化参数来控制PGA区中各种类型的SQL工作区的大小。

但如何为这些参数设置一个合理的值是非常困难的,因为用户将执行哪些类型的SQL语句,以及这些SQL语句在执行过程的行为和状态往往是难以准确预料的。因此,在Oracle 9i后,引入了一个新的参数PGA_AGGREGATE_ TARGET,以实现自动管理PGA。这样只需要为PGA_AGGREGATE_TARGET参数指定一个值,Oracle就会将该值作为所有SQL工作区大小总和的限制,在这个限制的基础上,Oracle会为各个进程自动设置PGA区大小。

2. 会话内存区

会话内存区用于保存用户会话的变量(登录信息),以及其他与会话相关的信息。如果数据库处于共享服务器连接模式下,会话内存区将位于SGA区而不是PGA区,因为会话信息会被所有的共享服务进程所使用。

如果数据库处于专用服务器连接模式下,会话内存区将保存在为这个会话提供服务的专用服务进程的PGA区中。因为这时只有该服务进程需要使用该会话的会话信息。