14. 内存架构
14.1. Oracle是数据库内存结构简介
当实例启动时,Oracle数据库会分配一个内存区域并启动后台进程。这个内存区域存储以下信息:
- 程序代码
- 每个已连接会话的信息,即使它当前未活跃
- 程序执行期间需要的信息,例如,正在从中提取行的查询的当前状态
- 锁数据等在进程间共享和通信的信息
- 缓存的数据,如数据块和重做记录,这些数据也存在于磁盘上
14.1.1. 基础内存结构
与Oracle数据库相关联的基本内存结构包括:
-
系统全局区(SGA)
SGA是一组共享内存结构,称为SGA组件,包含一个Oracle数据库实例的数据和控制信息。SGA被所有服务器和后台进程共享。存储在SGA中的数据示例包括缓存的数据块和共享SQL区域。 -
程序全局区(PGA)
PGA是一个非共享内存区域,包含专门供一个Oracle进程使用的数据和控制信息。当启动一个Oracle进程时,Oracle数据库会创建PGA。每个服务器进程和后台进程都有一个PGA。各个PGA的集合构成了整个实例PGA,或称为实例PGA。数据库初始化参数设置了实例PGA的大小,而不是单个PGA的大小。 -
用户全局区(UGA)
UGA是与用户会话相关联的内存。 -
软件代码区域
软件代码区域是用于存储正在运行或可以运行的代码的内存部分。Oracle数据库代码存储在通常与用户程序不同的位置——一个更专属或受保护的位置。
图14-1说明了这些内存结构之间的关系。
14.1.2. Oracle数据库内存管理
内存管理涉及在数据库需求变化时维护Oracle实例内存结构的最优大小。Oracle数据库根据内存相关初始化参数的设置来管理内存。内存管理的基本选项如下:
-
自动内存管理
您指定实例内存的目标大小。数据库实例自动调整到目标内存大小,在SGA和实例PGA之间按需重新分配内存。 -
自动共享内存管理
这种管理模式是部分自动化的。您为SGA设置一个目标大小,然后可以选择为PGA设置一个总体目标大小,或者单独管理PGA工作区。 -
手动内存管理
您不是设置总内存大小,而是设置许多初始化参数来单独管理SGA和实例PGA的各个组件。
如果您使用数据库配置助手(DBCA)创建数据库并选择基本安装选项,则自动内存管理是默认设置。
14.2. 用户全局区概述
UGA是会话内存,它是为会话变量分配的内存,如登录信息以及数据库会话所需的其他信息。本质上,UGA存储了会话状态。图14-2描述了UGA。
如果一个会话将一个PL/SQL包加载到内存中,那么UGA包含包的状态,这是在特定时间存储在所有包变量中的值的集合(参见第8-6页的“PL/SQL包”)。当包子程序更改变量时,包状态会发生变化。默认情况下,包变量是唯一的,并且会持续存在于会话的生命周期内。
OLAP页面池也存储在UGA中。这个池管理OLAP数据页面,这些页面等同于数据块。页面池在OLAP会话开始时分配,在会话结束时释放。当用户查询维度对象(如立方体)时,OLAP会话会自动打开。
UGA必须在会话的生命周期内对数据库会话可用。因此,当使用共享服务器连接时,UGA不能存储在PGA中,因为PGA特定于单个进程。因此,在使用共享服务器连接时,UGA存储在SGA中,这使得任何共享服务器进程都可以访问它。当使用专用服务器连接时,UGA存储在PGA中。
14.3. 程序全局区概述
PGA是特定于一个操作系统进程或线程的内存,系统上的其他进程或线程不共享。因为PGA是进程特定的,所以它从不在SGA中分配。
PGA是一个内存堆,包含专用或共享服务器进程所需的会话依赖变量。服务器进程在PGA中分配它所需要的内存结构。
PGA的一个比喻是一个文件管理员使用的临时柜台工作区。在这个比喻中,文件管理员是代表客户(客户端进程)工作的服务器进程。管理员清理柜台的一部分,使用工作区来存储有关客户请求的详细信息,并按客户要求对文件夹进行排序,然后在工作完成后放弃这个空间。图14-3显示了一个未配置为共享服务器的实例的实例PGA(所有PGA的集合)。您可以使用初始化参数来设置实例PGA的目标最大大小(参见第18-17页的“内存管理方法摘要”)。各个PGA可以根据需要增长,直到达到这个目标大小。
注意:后台进程也会分配自己的PGA。这里的讨论仅关注服务器进程的PGA。
14.3.1. PGA内容
PGA被细分为不同的区域,每个区域都有不同的目的。图14-4显示了一个专用服务器会话的PGA可能包含的内容。并非所有情况下都会存在PGA的所有区域。
14.3.1.1. 私有SQL区
一个私有SQL区域保存了关于解析后的SQL语句和其他会话特定信息的处理。当服务器进程执行SQL或PL/SQL代码时,该进程使用私有SQL区域来存储绑定变量值、查询执行状态信息和查询执行工作区。不要将位于UGA中的私有SQL区域与位于SGA中存储执行计划的共享SQL区域混淆。同一个或不同会话中的多个私有SQL区域可以指向SGA中的单个执行计划。例如,一个会话中执行20次SELECT * FROM employees
和另一个会话中执行10次相同查询可以共享同一个计划。每次执行的私有SQL区域是不共享的,并且可能包含不同的值和数据。游标是指向特定私有SQL区域的名称或句柄。如图14-5所示,您可以将游标视为客户端的指针和服务器端的状态。由于游标与私有SQL区域密切相关,这两个术语有时可以互换使用。
一个私有SQL区域被划分为以下区域:
-
运行时区域
这个区域包含查询执行状态信息。例如,运行时区域跟踪在全表扫描中到目前为止检索的行数。
Oracle数据库在执行请求的第一步创建运行时区域。对于DML语句,运行时区域在SQL语句关闭时释放。 -
持久区域
这个区域包含绑定变量值。绑定变量值在执行SQL语句时在运行时提供。只有当游标关闭时,持久区域才被释放。
客户端进程负责管理私有SQL区域。私有SQL区域的分配和释放在很大程度上取决于应用程序,尽管客户端进程可以分配的私有SQL区域数量受到初始化参数OPEN_CURSORS的限制。
尽管大多数用户依赖数据库实用程序的自动游标处理,但Oracle数据库的程序化接口为开发人员提供了对游标更多的控制。通常,应用程序应该关闭所有不再使用的打开的游标,以释放持久区域,并最小化应用程序用户所需的内存。
14.3.1.2. SQL工作区
工作区是PGA内存的私有分配,用于内存密集型操作。例如,排序操作符使用排序区域对一组行进行排序。同样,哈希连接操作符使用哈希区域从其左侧输入构建哈希表,而位图合并使用位图合并区域来合并从多个位图索引扫描检索到的数据。
示例14-1显示了员工和部门的连接及其查询计划。
在示例14-1中,运行时区域跟踪全表扫描的进度。会话在哈希区域执行哈希连接以匹配两个表中的行。ORDER BY排序发生在排序区域。
如果操作符要处理的数据量不适合工作区,则Oracle数据库将输入数据分成较小的片段。通过这种方式,数据库在内存中处理一些数据片段,同时将其余部分写入临时磁盘存储,以便以后处理。
当启用自动PGA内存管理时,数据库会自动调整工作区大小。您也可以手动控制和调整工作区的大小。有关更多信息,请参见第18-15页的“内存管理”。通常,较大的工作区可以显著提高操作符的性能,代价是更高的内存消耗。理想情况下,工作区的大小足以容纳输入数据和其关联SQL操作符分配的辅助内存结构。如果不是这样,响应时间会增加,因为部分输入数据必须缓存在磁盘上。在极端情况下,如果工作区的大小与输入数据的大小相比太小,则数据库必须对数据片段执行多次遍历,显著增加响应时间。
14.3.2. 专用服务器模式与共享服务器模式下的PGA的使用
PGA内存分配取决于数据库是使用专用还是共享服务器连接。表14-1显示了它们之间的差异。
14.4. 系统全局区概述
SGA是一个读写内存区域,与Oracle后台进程一起构成数据库实例。所有代表用户执行的服务器进程都可以读取实例SGA中的信息。在数据库操作期间,有多个进程会向SGA写入数据。
注意:服务器和后台进程并不位于SGA内,而是存在于一个单独的内存空间中。
每个数据库实例都有自己的SGA。Oracle数据库在实例启动时自动分配SGA内存,并在实例关闭时回收内存。当您通过SQL*Plus或Oracle Enterprise Manager启动一个实例时,SGA的大小会显示如下例所示:
如图14-1所示,SGA由多个内存组件组成,这些内存池用于满足特定类别的内存分配请求。除了重做日志缓冲区之外,所有SGA组件都以称为颗粒的连续内存单元来分配和释放空间。颗粒大小是平台特定的,由总SGA大小决定。您可以查询V$SGASTAT视图以获取有关SGA组件的信息。最重要的SGA组件包括:
- 数据库缓冲区高速缓存
- 重做日志缓冲区
- 共享池
- 大池(Large Pool)
- Java池(Java Pool)
- Streams池(Streams Pool)
- 固定SGA(Fixed SGA)
14.4.1. 数据库缓冲区高速缓存
数据库缓冲区高速缓存,也称为缓冲区高速缓存,是存储从数据文件中读取的数据块副本的内存区域。缓冲区是主内存地址,缓冲区管理器在其中临时缓存当前或最近使用的数据块。所有同时连接到数据库实例的用户共享对缓冲区高速缓存的访问。
Oracle数据库使用缓冲区高速缓存来实现以下目标:
-
优化物理I/O:数据库在缓存中更新数据块,并将更改的元数据存储在重做日志缓冲区中。提交事务后,数据库将重做缓冲区写入磁盘,但不会立即将数据块写入磁盘。相反,数据库写入器(DBW)在后台执行延迟写入。
-
保留频繁访问的数据块在缓冲区高速缓存中,并将不常访问的数据块写入磁盘
当启用数据库智能闪存高速缓存(闪存高速缓存)时,缓冲区高速缓存的一部分可以存储在闪存高速缓存中。这种缓冲区高速缓存扩展存储在闪存磁盘设备上,这是一种使用闪存的固态存储设备。数据库可以通过在闪存中缓存缓冲区而不是从磁性磁盘读取来提高性能。
注意:数据库智能闪存高速缓存仅在Solaris和Oracle企业Linux中可用。
14.4.1.1. 缓冲区状态
数据库使用内部算法来管理缓存中的缓冲区。一个缓冲区可以处于以下互斥状态之一:
-
未使用
缓冲区可供使用,因为它从未被使用过或当前未使用。这种类型的缓冲区对数据库来说使用起来最简单。 -
干净
缓冲区之前被使用过,现在包含一个块在某个时间点的一致性读版本。该块包含数据,但是是“干净”的,因此不需要检查点。数据库可以固定该块并重用它。 -
脏
缓冲区包含尚未写入磁盘的修改数据。数据库必须在重用之前对该块进行检查点。
每个缓冲区都有一个访问模式:固定或自由(未固定)。一个缓冲区在缓存中被“固定”,以便在用户会话访问它时不会从内存中老化出去。多个会话不能同时修改一个固定的缓冲区。
数据库使用一个复杂的算法来使缓冲区访问高效。脏缓冲区和非脏缓冲区的指针存在于同一个最近最少使用(LRU)列表上,该列表有一个热端和冷端。冷缓冲区是最近未被使用的缓冲区。热缓冲区是经常被访问并且最近被使用的缓冲区。
注意:概念上只有一个LRU列表,但为了并发,数据库实际上使用了几个LRU列表。
14.4.1.2. 缓冲模式
当客户端请求数据时,Oracle 数据库以以下任一模式从数据库缓冲区缓存中检索缓冲区:
■ 当前模式
当前模式获取,也称为 db 块获取,是检索缓冲区缓存中当前块的状态。例如,如果一个未提交的事务更新了一个块中的两行,那么当前模式获取将检索包含这些未提交行的块。数据库在修改语句期间最频繁地使用 db 块获取,这些语句必须仅更新块的当前版本。
■ 一致性模式
一致性读取获取是检索块的一致性版本。这种检索可能会使用 undo 数据。例如,如果一个未提交的事务更新了一个块中的两行,并且如果一个单独会话中的查询请求该块,那么数据库将使用 undo 数据创建一个不包含未提交更新的块的一致性版本(称为一致性读取克隆)。通常,查询以一致性模式检索块。
14.4.1.3. 缓冲I/O
逻辑 I/O,也称为缓冲区 I/O,指的是对缓冲区缓存中缓冲区的读写操作。当请求的缓冲区在内存中未找到时,数据库执行物理 I/O 将缓冲区从闪存缓存或磁盘复制到内存中,然后执行逻辑 I/O 来读取缓存的缓冲区。
缓冲区写入:数据库写入进程(DBWn)在以下情况下定期将冷的、脏的缓冲区写入磁盘:
■ 服务器进程在数据库缓冲区缓存中找不到干净的缓冲区来读取新的块。
随着缓冲区变脏,空闲缓冲区的数量减少。如果空闲缓冲区的数量低于内部设定的阈值,并且需要干净的缓冲区,服务器进程会通知 DBWn 进行写入。
数据库利用最近最少使用(LRU)算法来决定哪些脏缓冲区需要被写入。当脏缓冲区到达 LRU 列表的冷端时,数据库会将它们从 LRU 列表中移出并放入写队列。DBWn 将写队列中的缓冲区写入磁盘,如果可能的话,会使用多块写入以提高效率。这种机制防止 LRU 列表的末端因脏缓冲区过多而阻塞,并确保可以找到干净的缓冲区以便再次使用。
■ 数据库需要推进检查点位置,检查点是重做日志中必须从该位置开始进行实例恢复的点。
■ 表空间被设置为只读状态或被置于离线状态。
缓冲区读取:当干净或未使用的缓冲区数量较低时,数据库必须从缓冲区缓存中移除缓冲区。算法取决于是否启用了闪存缓存:
■ 闪存缓存未启用
数据库根据需要重用每个干净的缓冲区,覆盖它。如果稍后需要被覆盖的缓冲区,则数据库必须从磁盘读取它。
■ 闪存缓存已启用
DBWn 可以将干净缓冲区的主体写入闪存缓存,从而允许重用其内存中的缓冲区。数据库将缓冲区头部保留在主内存中的 LRU 列表中,以跟踪闪存缓存中缓冲区主体的状态和位置。如果稍后需要此缓冲区,则数据库可以从闪存缓存而不是从磁盘读取它。
当客户端进程请求缓冲区时,服务器进程会在缓冲区缓存中搜索该缓冲区。如果数据库在内存中找到缓冲区,则会发生缓存命中。搜索顺序如下:
- 服务器进程在缓冲区缓存中搜索整个缓冲区。
如果进程找到整个缓冲区,则数据库对此缓冲区执行逻辑读取。 - 服务器进程在闪存缓存 LRU 列表中搜索缓冲区头部。如果进程找到缓冲区头部,则数据库从闪存缓存到内存缓存中执行缓冲区主体的优化物理读取。
- 如果进程在内存中找不到缓冲区(缓存未命中),则服务器进程执行以下步骤:
a. 将块从数据文件复制到内存(物理读取)
b. 对已读取到内存中的缓冲区执行逻辑读取
图 14-6说明了缓冲区搜索顺序。扩展的缓冲区缓存包括内存中的缓冲区缓存(包含整个缓冲区)和闪存缓存(包含缓冲区主体)。在图中,数据库在缓冲区缓存中搜索缓冲区,未找到缓冲区,然后从磁盘读取到内存。
通常,通过缓存命中访问数据比通过缓存未命中要快。缓冲区缓存命中率衡量了数据库在不需要从磁盘读取的情况下在缓冲区缓存中找到请求块的频率。
数据库可以从数据文件或临时文件执行物理读取。从数据文件的读取之后会跟随逻辑 I/O。从临时文件的读取发生在内存不足时,数据库将数据写入临时表并在之后读取。这些物理读取绕过了缓冲区缓存,并且不会发生逻辑 I/O。
缓冲区访问次数:数据库使用触摸计数来衡量 LRU 列表中缓冲区的访问频率。这种机制允许数据库在缓冲区被固定时增加计数器,而不是不断地在 LRU 列表上重新排列缓冲区。
注意:数据库不会在内存中物理移动块。移动是列表上指针位置的变化。
当一个缓冲区被固定时,数据库会确定它的触摸计数最后一次增加是什么时候。如果计数是在三秒前增加的,那么计数会增加;否则,计数保持不变。三秒规则防止了对缓冲区的一系列固定被计为多次触摸。例如,一个会话可能在一个数据块中插入多行,但数据库将这些插入视为一次触摸。
如果一个缓冲区位于 LRU 的冷端,但其触摸计数很高,那么缓冲区会移动到热端。如果触摸计数很低,那么缓冲区就会从缓存中老化出去。
缓冲区和全表扫描:当缓冲区必须从磁盘读取时,数据库将缓冲区插入到 LRU 列表的中间。这样,热块可以保留在缓存中,因此不需要再次从磁盘读取。
全表扫描提出了一个问题,它会顺序读取表高水位线下的所有行(见第 12-27 页的“段空间和高水位线”)。假设一个表段中的块的总大小大于缓冲区缓存的大小。对这个表的完整扫描可能会清空缓冲区缓存,阻止数据库维护频繁访问块的缓存。
作为对大表进行全扫描的结果而读入数据库缓存的块,与其他类型的读取不同。这些块立即可供重用,以防止扫描有效地清空缓冲区缓存。
在默认行为不期望的罕见情况下,您可以更改表的 CACHE 属性。在这种情况下,数据库不会强制或固定缓冲区缓存中的块,而是像任何其他块一样让它们从缓存中老化出去。使用此选项时要小心,因为对大表的全扫描可能会清除缓存中的大部分其他块。
14.4.1.4. 缓冲池
缓冲池是一组缓冲区的集合。数据库缓冲区缓存被划分为一个或多个缓冲池。
您可以手动配置单独的缓冲池,这些缓冲池要么在缓冲区缓存中保留数据,要么在用完数据块后立即使缓冲区可用于新数据。然后,您可以将特定的模式对象分配给适当的缓冲池,以控制块如何从缓存中老化出去。
可能的缓冲池如下:
■ 默认池
这个池是通常缓存块的位置。除非您手动配置单独的池,否则默认池是唯一的缓冲池。
■ 保留池
这个池是为那些频繁访问但因空间不足而从默认池中老化出去的块设计的。保留缓冲池的目标是在内存中保留对象,从而避免 I/O 操作。
■ 回收池
这个池是为那些不经常使用的块设计的。回收池可以防止对象在缓存中占用不必要的空间。
数据库有一个标准块大小(见第 12-7 页的“数据库块大小”)。您可以创建一个块大小与标准大小不同的表空间。每个非标准块大小都有自己的池。Oracle 数据库以与默认池相同的方式管理这些池中的块。
图 14-7 显示了使用多个池时缓冲区缓存的结构。缓存包含默认、保留和回收池。默认块大小为 8 KB。缓存包含使用非标准块大小 2 KB、4 KB 和 16 KB 的表空间的单独池。
14.4.2. 重做日志缓冲区
重做日志缓冲区是 SGA 中的一个循环缓冲区,用于存储描述对数据库所做更改的重做日志条目。重做日志条目包含重新构建或重做数据库中由 DML 或 DDL 操作所做的更改所需的信息。数据库恢复将重做日志条目应用于数据文件,以重建丢失的更改。Oracle 数据库进程将重做日志条目从用户内存空间复制到 SGA 中的重做日志缓冲区。重做日志条目在缓冲区中占用连续的、顺序的空间。后台进程日志写入器(LGWR)将重做日志缓冲区写入磁盘上的活动在线重做日志组。图 14-8 显示了这种重做缓冲区活动。
LGWR 将重做日志顺序写入磁盘,而 DBWn 执行数据块的分散写入磁盘。分散写入通常比顺序写入慢得多。由于 LGWR 让用户无需等待 DBWn 完成其慢速写入,数据库提供了更好的性能。
LOG_BUFFER
初始化参数指定 Oracle 数据库在缓冲重做条目时使用的内存量。与其他 SGA 组件不同,重做日志缓冲区和固定 SGA 缓冲区不会将内存分割成颗粒。
14.4.3. 共享池
共享池缓存了各种类型的程序数据。例如,共享池存储了解析后的 SQL、PL/SQL 代码、系统参数和数据字典信息。共享池参与数据库中发生的几乎每个操作。例如,如果用户执行一个 SQL 语句,那么 Oracle 数据库就会访问共享池。
共享池被划分为多个子组件,其中最重要的子组件如图 14-9 所示。
本节包括以下主题:
■ 库缓存(Library Cache)
■ 数据字典缓存(Data Dictionary Cache)
■ 服务器结果缓存(Server Result Cache)
■ 保留池(Reserved Pool)
14.4.3.1. 库缓存
库缓存是共享池内存结构,用于存储可执行的 SQL 和 PL/SQL 代码。此缓存包含共享 SQL 和 PL/SQL 区域以及控制结构,如锁和库缓存句柄。在共享服务器架构中,库缓存还包含私有 SQL 区域。当执行 SQL 语句时,数据库尝试重用先前执行过的代码。如果库缓存中存在 SQL 语句的解析表示,并且可以共享,那么数据库重用该代码,称为软解析或库缓存命中。否则,数据库必须构建应用程序代码的新可执行版本,称为硬解析或库缓存未命中。
共享SQL区域
数据库在以下SQL区域中表示它运行的每个 SQL 语句:
■ 共享SQL区域
数据库使用共享 SQL 区域处理SQL语句的首次出现。这个区域对所有用户都是可访问的,包含语句解析树和执行计划。对于唯一的语句,只存在一个共享SQL区域。
■ 私有SQL区域
每个发出 SQL 语句的会话在其PGA中都有一个私有SQL区域(见第 14-5 页的“私有SQL区域”)。每个提交相同语句的用户都有一个指向相同共享 SQL区域的私有SQL区域。因此,多个位于不同PGA中的私有SQL区域可以与相同的共享SQL区域相关联。
数据库自动确定应用程序何时提交相似的SQL语句。数据库考虑用户直接发出的SQL语句和应用程序发出的SQL语句,以及其他语句内部发出的递归 SQL语句。
数据库执行以下步骤:
-
检查共享池中是否存在语法和语义完全相同的语句的共享SQL区域:
■ 如果存在相同的语句,则数据库使用共享SQL区域执行该语句的后续新实例,从而减少内存消耗。
■ 如果不存在相同的语句,则数据库在共享池中分配一个新的共享SQL区域。具有相同语法但不同语义的语句使用子游标。
在任何情况下,用户的私有SQL区域都指向包含语句和执行计划的共享SQL区域。 -
代表会话分配私有 SQL 区域
私有SQL区域的位置取决于为会话建立的连接。如果会话通过共享服务器连接,则私有SQL区域的一部分保留在SGA中。
图14-10显示了一个专用服务器架构,其中两个会话在它们自己的PGA中保留相同的SQL语句副本。在共享服务器中,此副本位于UGA中,UGA位于大池中,或者在没有大池存在时位于共享池中。
程序单元和库缓存:库缓存保存 PL/SQL 程序和 Java 类的可执行形式。这些项目统称为程序单元。
数据库以类似于 SQL 语句的方式处理程序单元。例如,数据库分配一个共享区域来保存 PL/SQL 程序的解析、编译形式。数据库分配一个私有区域来保存特定于运行程序的会话的值,包括局部、全局和包变量,以及执行 SQL 的缓冲区。如果多个用户运行相同的程序,那么每个用户维护自己私有 SQL 区域的单独副本,该区域保存特定于会话的值,并访问单个共享 SQL 区域。
数据库处理 PL/SQL 程序单元内的单个 SQL 语句,如前所述。尽管这些 SQL 语句源自 PL/SQL 程序单元,但它们使用共享区域来保存它们的解析表示,并为运行该语句的每个会话提供一个私有区域。
在共享池中分配和重用内存:当新的 SQL 语句被解析时,数据库会分配共享池内存。内存大小取决于语句的复杂性。
通常情况下,共享池中的一项会保留直到根据 LRU(最近最少使用)算法被移除。数据库允许多个会话使用的共享池项保留在内存中,只要它们有用,即使创建该项的进程终止了。这种机制最小化了 SQL 语句的开销和处理。
如果需要为新项腾出空间,那么数据库会为不常用项释放内存。即使对应的共享 SQL 区域关联的游标是打开的并且一段时间未被使用,共享 SQL 区域也可以从共享池中移除。如果随后使用打开的游标来执行其语句,那么 Oracle 数据库会重新解析该语句并分配一个新的共享 SQL 区域。
在以下情况下,数据库还会从共享池中移除共享 SQL 区域:
■ 如果为表、表集或索引收集统计信息,那么默认情况下,数据库会在一段时间后逐渐移除所有包含引用被分析对象的语句的共享 SQL 区域。下次运行已移除的语句时,数据库会在新的共享 SQL 区域中解析它,以反映模式对象的新统计信息。
■ 如果在 SQL 语句中引用了模式对象,并且该对象稍后被 DDL 语句修改,那么数据库会使共享 SQL 区域失效。优化器在下次运行该语句时必须重新解析。
■ 如果更改了全局数据库名称,那么数据库会从共享池中移除所有信息。
您可以使用 ALTER SYSTEM FLUSH SHARED_POOL
语句手动移除共享池中的所有信息,以评估实例重启后可以预期的性能。
14.4.3.2. 数据字典缓存
数据字典是包含有关数据库、其结构和用户参考信息的数据库表和视图的集合。Oracle 数据库在解析 SQL 语句期间频繁访问数据字典。Oracle 数据库访问数据字典的频率如此之高,以至于指定了以下特殊的内存位置来保存字典数据:
■ 数据字典缓存
这个缓存保存有关数据库对象的信息。该缓存也被称为行缓存,因为它以行而不是缓冲区的形式保存数据。
■ 库缓存
所有服务器进程共享这些缓存以访问数据字典信息。
14.4.3.3. 服务器结果缓存
与缓冲池不同,服务器结果缓存保存的是结果集而不是数据块。服务器结果缓存包含 SQL 查询结果缓存和 PL/SQL 函数结果缓存,它们共享相同的基础设施。
客户端结果缓存与服务器结果缓存不同。客户端缓存在应用程序级别配置,并且位于客户端内存中,而不是数据库内存中。
SQL查询结果缓存:数据库可以将查询及其片段的结果存储在 SQL 查询结果缓存中,并将缓存的结果用于未来的查询和查询片段。大多数应用程序都可以从这种性能提升中受益。
例如,假设一个应用程序重复运行相同的 SELECT 语句。如果结果被缓存,那么数据库会立即返回它们。通过这种方式,数据库避免了重新读取块和重新计算结果的昂贵操作。每当事务修改了构建该缓存结果所使用的数据库对象的数据或元数据时,数据库会自动使缓存的结果失效。
用户可以使用 RESULT_CACHE 提示来注释查询或查询片段,以指示数据库应该将结果存储在 SQL 查询结果缓存中。RESULT_CACHE_MODE 初始化参数决定 SQL 查询结果缓存是用于所有查询(可能的情况下)还是仅用于注释了的查询。
PL/SQL 函数结果缓存:PL/SQL 函数结果缓存存储函数的结果集。如果不使用缓存,1000次调用一个函数,每次调用需要1秒,那么总共需要1000秒。如果使用缓存,1000次函数调用(使用相同的输入参数)可能总共只需要1秒。适合结果缓存的好候选者是那些频繁被调用且依赖于相对静态数据的函数。
PL/SQL 函数代码可以包含一个请求来缓存其结果。在调用这个函数时,系统会检查缓存。如果缓存中包含了使用相同参数值的先前函数调用的结果,那么系统会将缓存的结果返回给调用者,并不会重新执行函数体。如果缓存中不包含结果,那么系统会执行函数体,并将结果(对于这些参数值)添加到缓存中,然后再将控制权返回给调用者。
注意:您可以指定用于计算缓存结果的数据库对象,以便如果它们中的任何一个被更新,缓存的结果就会变得无效,并且必须重新计算。
缓存可能会累积很多结果——对于每次调用结果缓存函数的每个唯一参数值组合都有一个结果。如果数据库需要更多内存,那么它会使一个或多个缓存结果老化并被淘汰。
14.4.3.4. 保留池
保留池是共享池中的一个内存区域,Oracle 数据库可以使用它来分配大块的连续内存。
从共享池分配内存是按块进行的。分块允许大型对象(超过 5 KB)被加载到缓存中,而不需要一个单独的连续区域。通过这种方式,数据库减少了因碎片化而导致的连续内存不足的可能性。不常见的情况是,Java、PL/SQL 或 SQL 游标可能会从共享池分配大于 5 KB 的内存。为了最有效地进行这些分配,数据库将共享池的一小部分隔离出来作为保留池。
14.4.4. 大池
大池是一个可选的内存区域,用于分配比共享池更适合的更大内存。大池可以为以下内容提供大内存分配:
■ 共享服务器和 Oracle XA 接口的 UGA(用于事务与多个数据库交互的地方)
■ 并行执行语句时使用的消息缓冲区
■ Recovery Manager (RMAN) I/O 奴隶的缓冲区
通过为共享 SQL 从大池分配会话内存,数据库避免了因缩小共享 SQL 缓存而引起的性能开销。通过为 RMAN 操作、I/O 服务器进程和并行缓冲区分配大缓冲区内存,大池可以比共享池更好地满足大内存请求。图 14-11 是大池的图形表示。
大池与共享池中的保留空间不同,后者使用与其他从共享池分配的内存相同的 LRU(最近最少使用)列表。大池没有 LRU 列表。内存块被分配后,直到使用完毕之前都不能被释放。一旦一块内存被释放,其他进程就可以使用它。
14.4.5. Java池
Java 池是内存中的一个区域,用于存储在 Java 虚拟机(JVM)中所有特定于会话的 Java 代码和数据。这些内存包括在调用结束时迁移到 Java 会话空间的 Java 对象。
对于专用服务器连接,Java 池包括每个 Java 类的共享部分,包括方法和只读内存(如代码向量),但不包括每个会话的每会话 Java 状态。对于共享服务器,该池包括每个类的共享部分和一些用于每个会话状态的 UGA(用户全局区域)。每个 UGA 根据需要增长和缩小,但总的 UGA 大小必须适合 Java 池空间。
Java 池顾问统计提供了有关用于 Java 的库缓存内存的信息,并预测 Java 池大小变化如何影响解析速率。当 statistics_level 设置为 TYPICAL(典型)或更高级别时,Java 池顾问会在内部自动开启。当顾问关闭时,这些统计数据会重置。
14.4.6. 流池
Streams 池存储缓冲队列消息,并为 Oracle Streams 捕获进程和应用进程提供内存。Streams 池专门由 Oracle Streams 使用。
除非您特别配置,否则 Streams 池的大小从零开始。根据 Oracle Streams 的需要,池大小会动态增长。
14.4.7. 固定SGA
固定SGA是一个内部的维护区域。例如,固定SGA包含:
■ 数据库和实例状态的一般信息,后台进程需要访问这些信息
■ 进程之间通信的信息,例如关于锁的信息(见第 9-17 页的“自动锁概述”)
固定SGA的大小由Oracle数据库设置,不能手动更改。不同版本之间的固定SGA大小可能会发生变化。
14.5. 软件代码区概述
软件代码区域是存储正在运行或可以运行的代码的内存部分。Oracle 数据库代码存储在一个通常比用户程序的位置更加独占和受保护的软件区域。软件区域通常大小固定,在软件更新或重新安装时才会发生变化。这些区域所需的大小因操作系统而异。软件区域是只读的,可以共享或非共享地安装。一些数据库工具和实用程序(如 Oracle Forms 和 SQL*Plus)可以共享安装,但有些则不能。如果可能的话,数据库代码是共享的,这样所有用户都可以访问它,而不需要在内存中有多个副本,从而减少主内存的使用,并总体上提高性能。如果运行在相同的计算机上,多个数据库实例可以使用相同的数据库代码区域,但使用不同的数据库。
注意:并非所有操作系统都提供共享安装软件的选项,例如,在运行 Windows 的个人电脑上就无法这样做。有关更多信息,请参见您的操作系统特定文档。