一、内存结构的概述
Oracle数据库为 程序代码、用户共享的数据 以及 每个连接用户的私有数据区域,创建和使用了多个内存区域。这些相关的基本内存结构包括System Global Area(SGA)、Program Global Area(PGA)、User Global Area(UGA)、Software Code Areas等。
在数据库实例启动时,首先在服务器内存中分配一个包含了实例数据和控制信息的共享内存区域(SGA),然后启动一组常驻内存的后台进程(执行维护任务,例如执行实例恢复、清理进程、编写重做缓冲区到磁盘等)。当客户端/应用程序连实例操作接数据库时,实例通过创建服务器进程(基于客户端请求执行工作,例如解析SQL查询并将放在共享池、创建和执行查询计划、从缓冲区缓存或磁盘中读取数据块),分配该进程专用的包含进程数据和控制信息的内存区域(PGA),来处理连接到该实例的客户端进程的请求。
二、SGA
即系统全局区域,是一组共享的内存结构,包含一个数据库实例的相关数据和控制信息,在实例启动时自动分配,在实例关闭时回收,包含一个数据库实例的相关数据和控制信息。所有的用户和服务器进程都共享。
SGA与后台进程一起构成一个数据库实例。服务器和后台进程是不驻留在SGA中的,而是存在于单独的内存空间。 SGA由多个内存组件组成,这些内存组件是用于满足特定类别的内存分配请求的内存池,其中包括Database Buffer Cache、Redo Log Buffer、In-Memory Column Store、Shared Pool、Large Pool、Java Pool、Streams Pool、Fixed SGA等。
SGA 中的数据字典缓存和其他信息会被实例的后台进程所访问,它们在实例启动后就固定在SGA中了,而且不会改变,所以这部分又称为固定SGA(Fixed SGA);Shared Pool、Java Pool、Large Pool和Streams Pool这几块内存区的大小是随系统参数设置而改变的,所以又通称为可变SGA(Variable SGA )。
SQL> show sga
Total System Global Area 4259229696 bytes --总系统全局区域大小
Fixed Size 2220072 bytes --固定SGA大小
Variable Size 1660948440 bytes --可变SGA大小
Database Buffers 2583691264 bytes --数据缓冲区缓存大小
Redo Buffers 12369920 bytes --重做日志缓冲区缓存大小
1、Database Buffer Cache
也叫Buffer Cache,即数据库缓冲区缓存或者缓冲区缓存,是存储从数据文件中读取的数据块的副本的内存区域。Oracle进程如果发现需要访问的数据块已经在Buffer Cache中,就可以直接读写在内存中的相应区域即相应的缓冲块,而无需读取数据文件以提高性能。
一个缓冲块(Buffer)的状态包括:Unused,不用的(从未被使用过或当前未被使用的块,是可供使用的块);Clean,干净的(较早被使用而现在包含了在一个时间点上是读一致版本的块,数据库可以定位并重用的块);Dirty,脏的(缓冲块中包含尚未写入磁盘的已修改数据。数据库必须在重新使用之前检查的块)
为了使缓冲区访问有效,必须确定要在内存中缓存哪些缓冲区以及从磁盘访问哪些缓冲区,数据库默认使用了基于LRU的替换算法,是通过两个重要的链表实现的:写链表和最近最少使用链表(the Least Recently Used,LRU)。写链表所指向的是所有脏数据块缓存(即被进程修改过,但还没有被回写到数据文件中去的数据块,此时缓冲中的数据和数据文件中的数据不一致)。而LRU链表指向的是所有空闲的缓存、正在被访问的缓存以及还没有来的及移入写链表的脏缓存。LRU列表有一个热端和冷端。冷端的缓冲区是最近没有使用的缓冲区,热端的缓冲区是经常被访问并且最近被使用的缓冲区。当脏缓冲区到达LRU的冷端时,数据库将它们从LRU移到写链表,然后数据库编写器(DBW)会进程周期性地将写链表中的缓冲区写入磁盘。
当客户进程请求一个缓冲区时,服务器进程会先搜索缓冲区缓存中的缓冲区,如果数据库在内存中找到缓冲区,则会发生缓存命中(cache hit),它就直接执行一个逻辑读取从内存中取该缓冲区。如果在buffer cache中没有找到该数据块,即未命中(cache miss),它就需要先执行物理读取从数据文件中取出该数据块到缓冲区缓存中,然后再执行逻辑读取才能访问该数据块。
在将数据块读取到缓存中时,服务器进程需要从空闲列表种找到一个适合大小的空闲缓存,如果空闲列表中没有适合大小的空闲缓冲块,就会从冷端开始查找LRU链表,在查找过程中,如果进程找到一个脏缓存块,它将这个缓存块移到写链表中去,然后继续查找;当它找到一个空闲块后,就从磁盘中将数据块读取到内存的缓存块中,并将这个缓存块移到LRU链表的中间;当进程查找到块数最大限制后还没有找到可供使用的缓存块,就停止查找LRU链表,并且通过信号通知DBW将脏缓存写入磁盘以提供可用的缓冲块;而当内存不足迫使数据库将数据写入临时表并在稍后读取数据时,就会出现一个临时文件。
数据库使用触摸计数来度量LRU列表上的缓冲区访问频率,该机制能够在缓冲块被命中时增加计数器,计数规则为三秒内的一和多次命中都计数为一次,触摸计数高的缓冲块就会移动到热端,移动时仅更改LRU列表的指针
数据库对表扫描时,默认情况下磁盘读取缓冲区插入到LRU列表的中间,而对于全表扫描,默认只有当表大小占缓冲区高速缓存的一小部分时才将小表加载到内存中;对于非常大的表,数据库通常使用直接读取的路径,该路径将直接加载到PGA中并绕过SGA,以避免填充缓冲区缓存;对于中等大小的表,如果它决定使用缓存读取,那么数据库将把块放在LRU列表的冷端,以防止有效的缓存块被清除。
2、Redo Log Buffer
即 重做日志缓冲区, 是SGA中的循环缓冲区,用于存储 描述对数据库所做更改 的 重做条目。重做记录是一种数据结构,它包含了通过DML或DDL操作对数据库进行重构或重做的所需信息。数据库恢复时将重做条目应用于数据文件以重建丢失的更改。
数据库进程将重做记录从用户内存空间复制到SGA中的重做日志缓冲区。重做记录在缓冲区中占据连续的、连续的空间。后台进程中的日志写入进程(LGWR)将重做日志缓冲区写到磁盘上活动的重做日志文件(Redo Log File)组中。
LGWR会按顺序写入磁盘,而DBW是将数据块分散写入磁盘。分散写入往往比顺序写入慢得多。由于LGWR使用户可以避免等待DBW完成其慢速写入,因此数据库可以提供更好的性能。当DBW将数据块写到磁盘上时,LGWR会按顺序重写到磁盘。分散的写往往比顺序写慢得多。由于LGWR使用户能够避免等待DBW完成其慢写操作,因此数据库提供了更好的性能。
3、In-Memory Column Store
即 内存列存储(IM列存储),是12C开始的一个可选的静态SGA池,在IM列存储中的数据以列状格式进行存储。内存中的列存储不会替换数据库缓冲区中的缓存,它们是相互补充的,两个存储区可以以不同的格式存储相同的数据。可以为表、物化视图、分区和表空间这些数据库对象启用内存列存储;在表空间级别启用内存列存储会自动启用内存列存储的表空间中的所有表和物化视图。
IM列存储使数据库能够在执行扫描、连接和聚合等操作时拥有更高的性能。可用于 对大型表格执行快速全面扫描;扫描大量的行和应用过滤器,例如<,>,=,和IN;从大量列中查询列的一小部分,例如从100列的表中选择5列;将一个小表加入大表中,特别是在连接条件过滤大部分行时。
4、Shared Pool
即 共享池,用于存放SQL语句、PL/SQL代码(程序单元)、数据字典、系统参数和其他控制信息。
共享池来缓存许多不同类型的数据。缓存的数据包括PL/SQL块和SQL语句、字典缓存数据、结果缓存数据和其他数据的文本和可执行形式。 共享池分为几个子组件:Library Cache、Data Dictionary Cache、Server Result Cache、Reserved Pool等。
4.1 Library Cache
即 库缓存,是一个存储最近引用的可执行SQL和PL/SQL代码的共享池内存结构,包含了共享SQL区域、PL/SQL区域、其他的如锁和库缓存句柄的控制结构。
4.1.1 Shared SQL Areas
即 共享SQL区域,Oracle会为每一条SQL语句的运行(每运行一条语句Oracle都会打开一个游标)提供一个共享SQL区(Shared SQL Areas)和私有SQL区(Private SQL Areas,属于PGA)。
共享SQL区存储的是最近执行的SQL语句、解析后的语法树和优化后的执行计划,后续执行相同的SQL语句就直接利用在共享SQL区中缓存的解析信息,不必再重复做语法解析。
私用SQL区存储的是在执行SQL语句的会话或用户的私有的相关信息,不同的会话即使执行相同的SQL语句也不会使用这些信息,比如绑定变量、执行环境和会话参数等。
Oracle在执行一条的SQL语句时:先为会话分配一个私有SQL区域;然后检查共享池,查看共享的SQL区域是否存在语法和语义相同的语句,如果存在则使用共享的SQL区域中的缓存信息来执行语句,这称为软解析;如果不存在在一个相同的即语法相同的语句,那么数据库将在共享池内存中分配一个新的共享SQL区域,这称为硬解析;如果存在相同语法但不同语义的语句则使用共享父游标生成子游标的方式来执行语句,也称为硬解析。
4.1.2 Program Units
即 程序单元, 库缓存中包含的PL/SQL程序和Java类的可执行形式,称为程序单元。
数据库处理程序单元类似于SQL语句,也会分配一个共享的区域来保存解析的、已编译的PL/SQL程序,同时也分配一个私有的区域来保存运行该程序的会话的信息,包括本地变量、全局变量和包变量以及执行SQL的缓冲区。每个运行相同的程序单元的用户,都共享一个共享区域,同时维护各自的私区域用户保存会话特定的变量值。
一般情况下,共享池中的项会一直保持,直到根据最近使用的(LRU)算法删除它。使用ALTER SYSTEM FLUSH SHARED_POOL语句可以手动删除共享池中的所有信息,生产环境慎用。
4.2 Data Dictionary Cache
即数据字典缓存,保存的是数据库相关的对象信息,包含数据库的结构、表和视图的定义、用户及其口令和权限等信息。所有的服务器进程共享这些缓存以访问数据字典信息。数据库在SQL语句的解析过程中通过访问该缓存来确定对象是否存在和具有权限等。字典缓存的内容是按行(Row)存储的,其他数据通常按数据块(Buffer)存储,所以又被称为 Row Cache,其信息可以通过 v$rowcache 查询。
4.3 Server Result Cache
即服务器结果缓存,存在于共享池中的内存池。与缓冲区(Buffer Cache)不同,服务器结果缓存包含的是结果集,而不是数据块。服务器结果缓存包含 SQL Query Result Cache 和 PL/SQL Function Result Cache,它们共享相同的基础结构。客户端结果缓存与服务器结果缓存不同,客户端缓存是在应用程序级别配置的,它位于客户机内存中,而不是在数据库内存中。
4.3.1 SQL Query Result Cache
即 SQL查询结果缓存,是存储查询和查询片段结果的服务器结果缓存的一个子集。一个重复运行同一个SELECT语句的应用程序,如果结果被缓存,那么数据库将立即得到返回,避免了重新读取块和重新计算结果的昂贵操作,大多数应用程序都得益于这种性能的改进。
执行查询时,数据库先搜索内存中的查询结果缓存来确认结果是否已经被缓存,如果结果存在则从直接内存中检索结果,而不是执行查询;如果结果没有被缓存则执行查询,将结果作为输出返回,然后将结果缓存在查询结果缓存中;当事务修改了用于构造查询缓存结果的数据库对象中的数据或元数据时,数据库会自动地使查询结果缓存中结果失效。
4.3.2 PL/SQL Function Result Cache
即 PL/SQL函数结果缓存,是存储函数结果集的服务器结果缓存的一个子集。如果没有缓存,每秒1000个函数的调用将花费1000秒。通过缓存,具有相同输入的1000个函数调用可能需要1秒。利用结果缓存而执行效果好调用,通常是依赖于相对静态的数据的函数。
PL/SQL函数代码可以包含缓存其结果的请求。在调用这个函数时,系统检查缓存。如果缓存包含了具有相同参数值的前一个函数调用的结果,则系统将缓存的结果返回给调用者,并且不会重新执行函数体。如果缓存不包含结果,则系统将执行该函数体,并将结果(对于这些参数值)添加到缓存中,然后将控制权返回给调用者。
缓存可以累积许多的结果,每个结果缓存是一个缓存函数和被调用的参数值的唯一组合。如果数据库需要更多的内存,它会老化一个或多个缓存的结果。
4.4 Reserved Pool
即 保留池,是共享池中的一个内存区域,Oracle数据库可以使用它来分配大量连续的内存块。
数据库从共享池中分配内存,块允许大型对象(大于5k)加载到缓存中,而不需要单个连续区域,这样,数据库就减少了由于碎片而耗尽连续内存的可能性。通常,Java、PL/SQL或SQL游标可能会从大于5kb的共享池中进行分配。为了使这些分配最有效地进行,数据库将少量的共享池隔离在保留池中。
5、Java Pool
即Java池,也是SGA中的一块可选内存区,它也属于SGA中的可变区,用于存储所有会话特定的Java代码和和Java虚拟机中的数据。该内存包括在结束调用时迁移到Java会话空间的Java对象。
对于专用服务器的连接,Java池包括每个Java类的共享部分,包括方法和只读内存,比如代码向量,但不包括每个会话的每个会话Java状态。对于共享服务器,池包括每个类的共享部分以及每个会话状态的一些UGA。每个UGA都根据需要增长和收缩,但是UGA的总大小必须适合于Java池空间。
Java Pool Advisor统计信息提供了有关于Java的库缓存内存的信息,并预测Java池大小的变化如何影响分析效率。当statistics_level设置为TYPICAL或更高时,Java Pool Advisor在内部打开。当advisor关闭时,这些统计信息将被重置。
6、Large Pool
即大池,是SGA中的一块可选内存池,根据需要时配置。数据库实例使用以下功能时应考虑配置大型池:为共享服务器配置大型池来分配用户全局区域(UGA);为并行查询配置大型池来缓存并行执行消息缓冲区;为恢复管理器(RMAN)的备份和恢复配置大型池来缓存I/O缓冲区;使用分布式事务处理的Oracle XA接口时配置大型池。
如果没有创建大池,则这些需要大量内存空间的操作将占用share pool的内存,通过在大池中分配会话内存给共享服务、Oracle XA、并行查询以及备份和恢复操作,可以减少share pool因为频繁的为大对象分配和回收内存而产生的碎片,提高shared pool的使用效率,防止由于共享SQL缓存收缩导致的性能消耗。
大池是属于SGA的可变区,没有LRU链表,当数据库将大型池的内存分配给一个会话时,在会话完成使用之前不能被释放,且只要一块内存被释放,其他进程就可以使用它。
7、Streams Pool
即流池,也是可选内存区,属于SGA中的可变区。流池用于存储缓冲队列消息,并为Oracle流捕获进程和应用程序提供内存。流池仅供Oracle流使用。除非专门配置它,否则流池的大小从0开始并根据Oracle流的需要动态增长。
8、Fixed SGA
即 固定SGA,是一个内部管理区域。
固定的SGA包含有关后台进程需要访问的数据库和实例状态的一般信息和进程之间交流的信息如关于锁的信息等
固定SGA的大小由Oracle数据库设置,不能手动更改。 固定SGA的大小可以从释放到释放。
三、PGA
即进程全局区域,是一个内存区域,在一个服务器进程或后台进程启动是创建,包含服务器进程和后台进程的相关数据和控制信息;PGA是特定于进程的非共享的内存区域,一个PGA只能被拥有它的那个进程所访问;PGA不在SGA中分配。
PGA被细分为不同的区域,每个区域都有不同的作用,包括Private SQL Area和SQL Work Areas等。
1、Private SQL Area
即 私有SQL区域,一个私有SQL区域包含关于一个已解析的SQL语句和其他特定于会话的信息的处理信息。当服务器进程执行SQL或PL/SQL代码时,进程使用专用SQL区域存储绑定变量值,查询执行状态信息和查询执行工作区域。
不要将PGA中的私有SQL区域与在SGA中存储执行计划的共享SQL区域混淆。相同或不同会话中的多个私有SQL区域可以指向SGA中的单个执行计划来共享,但每次执行的私有SQL区域因为可能包含不同的值和数据而不共享。
游标是特定私有SQL区域的名称或句柄,可以将游标视为客户端上的指针,并将其视为服务器端的状态。由于游标与私有SQL区域密切相关,因此这些术语有时可以互换使用。
一个私有SQL区域又被划分为run-time area和persistent area两个子区域。
1.1 The run-time area
即运行时区域,该区域包含查询执行状态信息。例如,运行时区域跟踪到目前为止在全表扫描中检索到的行数。
Oracle数据库创建运行时区域作为执行请求的第一步。对于DML语句,当SQL语句关闭即执行结束时,运行时区域将被释放。
1.2 The persistent area
即持久化区域,该区域包含绑定变量值。在执行语句时向SQL语句提供绑定变量值。只有当游标关闭时,持久区域才被释放。
客户端进程负责管理私有SQL区域,它的分配和释放很大程度上取决于应用程序,尽管客户端进程可以分配的私有SQL区域的数量受到初始化参数OPEN_CURSORS的限制。
虽然大多数用户依赖数据库实用程序的自动游标处理,但Oracle数据库编程接口为开发人员提供了对游标的更多控制。通常,应用程序应关闭所有打开的游标,这些游标不会再次用于释放持久区域,并尽量减少应用程序用户所需的内存。
2、SQL Work Areas
工作区域是用于内存密集型操作的PGA内存的私有分配。当启用自动PGA内存管理时,数据库会自动调整工作区域的大小,也可以手动控制和调整工作区域的大小。
例如,Sort操作符使用排序区域对一组行进行排序。类似地,Hash连接操作符使用哈希区域从它的左侧输入构建一个哈希表,而bitmap merge使用位图合并区域来合并从多个位图索引的扫描中检索到的数据。
一般来说,较大的工作区域可以显著的提高性能,但会以较高的内存消耗为代价。最佳的是,工作区域的大小足以容纳由其关联的SQL操作符分配的输入数据和辅助内存结构。如果没有,响应时间增加,因为输入数据的一部分必须缓存到磁盘上;而在极端情况下,如果工作区域的大小与输入数据大小相比太小,则数据库必须对数据块执行多次传递,从而显着增加响应时间。
三、UGA
即 用户全局区域,UGA是会话内存,它是为会话变量分配的内存,如登录信息以及数据库会话所需的其他信息。实质上,UGA存储了会话状态。
在会话期间,必须向数据库会话提供UGA,因此,在使用共享服务器连接时UGA不能存储在PGA中,因为PGA特定于单个进程,所以UGA在使用共享服务器连接时存储在SGA中,使任何共享服务器进程都能访问它。当使用专用服务器连接时,UGA存储在PGA中。
如果会话将PL/SQL包加载到内存中,则UGA包含包状态,即在特定时间内存储在所有包变量中的一组值。当包子程序更改变量时,包状态会发生变化。默认情况下,包变量是唯一的,并在会话的整个生命周期中保持不变。
OLAP page pool也存储在UGA中。这个池管理OLAP数据页面,这相当于数据块。页面池在OLAP会话开始时分配,并在会话结束时释放。每当用户查询多维度对象(如多维数据集)时,OLAP会话都会自动打开。
四、Software Code Areas
即 软件代码区域,软件代码区是内存的一部分,用于存储正在运行或可以运行的代码。 Oracle数据库代码存储在通常比用户程序的位置更独特和受保护的软件区域中。
软件区域通常是固定的,只有在更新或重新安装软件时才会更改。这些区域所需的大小因操作系统而异。
软件区域是只读的,可以安装共享或非共享。一些数据库工具和实用程序如SQL*Plus可以安装共享,但有些不能。在可能的情况下,共享数据库代码,这样所有用户都可以访问它,而不会在内存中有多个副本,从而减少主内存和整体性能的改善。如果在同一台计算机上运行,数据库的多个实例可以使用不同数据库的相同数据库代码区。