Oracle 内存结构

Oracle内存结构


一、概述
每当数据库启动,系统全局区(SGA)被分配,并启动了Oracle后台进程。
系统全局区用于数据库用户共享数据库信的内存区域,
后台进程和内存缓冲区称为Oracle实例,Oracle实例包含2种进程:用户进程和Oracle进程。
用户进程执行应用操作的代码,Oracle进程是执行用户进程和后台进程,对Oracle进行维护的服务器进程。
数据库拥有多个后台进程,其数量取决于数据库的配置。
每个后台进程都有一个跟踪文件。可以设置init.ora文件的background_dump_dest参数来规定文件位置。

Oracle内存结构分为系统全局区和程序全局区,下面逐一讲述。


二、系统全局区(SYSTEM Global Area)SGA
说明:SGA是Oracle数据库存放系统信息的一块内存区域。这样设计的主要原因是在内存中通信速度快、效率高。所有用户进程和服务进程均可访问这个内存结构。
包含

1、数据块缓冲存储区(Data Block Buffer Cache)
1.1、一个高速缓存区,用来存储从数据库中读取数据段的数据块(如表、索引、簇)副本。所有并发的连接到实例上的用户进程都将共享同一个数据缓存区。
1.2、块的大小由数据库服务器init.ora文件的DB_BLOCK_BUFFERS参数决定(用数据库块的个数表示),通常为数据库大小的1%~2%。
1.3、缓冲区大小由init.ora文件的db_cache_size参数决定。Oracle使用LRU算法管理。(Least Recently Used最近最少使用算法)。
1.4、当SGA大小不足以容纳最常用数据时会导致段争夺SGA的空间,数据缓冲区将出现较低命中率致使系统性能下降。

缓冲区分为3个池:
保持(keep)缓冲池 (长期保存在内存中,直到数据库被关闭为止。BUFFER_POOL_KEEP指定大小)
再生(recycle)缓冲池 (数据一旦用毕后就被换出内存中。BUFFER_POOL_RECYCLE指定大小)
默认(default)缓冲池 (数据使用LRU调度算法来换出。DB_CACHE_SIZE - BUFFER_POOL_REEP - BUFFER_POOL_RECYCLE 即为该缓冲池的大小)

缓冲区分为3种块:
数据高速缓存块由许多大小相等的缓存块组成,这些缓存块的大小和OS块大小相同。 这些缓存块分为 3 大类、
脏缓存块( Dirty buffers ):
脏缓存块中保存的时被修改过的缓存块。即当一条SQL语句对某个缓存块中的数据进行修改后,该缓存块就被标记为藏缓存块。
最后该脏缓存块被DBWn进程写入到硬盘的数据文件中,永久保留起来。
命中缓存块( Pinned buffers ):
命中缓存块中保存的是最近正在被访问的缓存块。它始终被保留中数据高速缓存中,不会被写入数据文件。
空闲缓存块(Free buffers):
该缓存块中没有数据,等待被写入数据。oracle从数据文件中读取数据后,寻找空闲缓存块,以便写入其中。

Oracle 通过 2 个列表(DIRTY、LRU)来管理缓存块
DIRTY 列表中保存已经被修改但还没有被写入到数据文件中的脏缓存块。
LRU 列表中保存所有的缓存块(还没有被移动到DIRTY列表中的脏缓存块、空闲缓存块、命中缓存块)。
当某个缓存块被访问后,该缓存块就被移动到LRU列表的头部,其他缓存块就向LRU列表的尾部移动。
放在最尾部的缓存块就最先被移出LRU列表。

数据高速缓存的工作原理过程是:
A、ORACLE在将数据文件中的数据块复制到数据高速缓存中之前,先在数据高速缓存中找空闲缓存块,以便容纳该数据块。Oracle 将从LRU列表的尾部开始搜索,直到找到所需的空闲缓存块为止。
B、如果先搜索到的是脏缓存块,将将该脏缓存块移动到DIRTY列表中,然后继续搜索。如果搜索到的是空闲缓存块,则将数据块写入,然后将该缓存块移动到DIRTY列表的头部。
C、如果能够搜索到足够的空闲缓存块,就将所有的数据块写入到对应的空闲缓存块中。则搜索写入过程结束。
D、如果没有搜索到足够的空闲缓存块,则ORACLE就先停止搜索,而是激活DBWn进程,开始将DIRTY列表中的脏缓存块写入到数据文件中。
E、已经被写入到数据文件中的脏缓存块将变成空闲缓存块,并被放入到LRU列表中。执行完成这个工作后,再重新开始搜索,直到找到足够的空闲缓存块为止。


2、重做日志缓冲区
为了加快访问和速度和工作效率,重做记录并不直接写入重做日志文件中,而是首先从数据高速缓存写入重做日志高速缓存。当重做日志高速缓存中的重做记录
达到一定数量或某个时间点时,再由LGWR进程分批写入重做日志文件中(即ORACLE 总是先日志后文件或先内存后磁盘)。由于重做日志文件是循环使用的。
所以当重做日志文件切换时,还会由ARCn(如果启用了归档日志模式)进程将即将要被覆盖的重做日志文件中的数据写入到归档日志文件中,作为备份。

SGA内一块被循环使用的缓冲区,用于记录数据库内的数据变化信息。这些信息以重做条目的形式进行存储。
Oracle利用重做条目内的信息就可以重做由INSERT,UPDATE,DELETE,DREATE,ALTER,DROP等操作对数据库进行的修改。
重做项描述对数据库进行的修改。它们写到联机重做日志文件中,以便在数据库恢复过程中用于向前滚动操作。
然而。在被写到联机重做日志文件之前,事务首先被记录在称作重做日志缓冲区的SGA中。
数据库可以周期滴分批想联机重做日志文件中写重做项的内容,其大小(字节为单位)有init.ora的LOG_BUFFER参数决定,可以在运行期间修改该参数。

SHOW PARAMETER LOG_BUFFER; ------查询重做日志缓存的大小

3、SQL共享池(Shared Pool)

工作原理:
  oracle将SQL语句提炼为两部分,一部分是SQL语句的静态部分,也就是SQL语句本身的关键词、所涉及的表名称以及表的列等。
另一部分就是SQL语句的动态部分,也就是SQL语句中的值(即表里的数据)。很明显的,整个数据库中所包含的对象数量是有限的,而其中所包含的数据则是无限的。
而正是这无限的数据导致了SQL语句的千变万化,也就是说在数据库运行的过程中,发生的所有SQL语句中,静态部分可以认为数量是有限的,而动态部分则是无限的。
而实际上,动态部分对解析的影响相比静态部分对解析的影响来说是微乎其微,也就是说通常情况下,对于相同的静态部分的SQL语句来说,
不同的动态部分所产生的解析结果(执行计划)基本都是一样的。这也就为oracle提高解析SQL语句的效率提供了方向。

  oracle会将用户提交来的SQL语句都缓存在内存中。每次处理新的一条SQL语句时,都会先在内存中查看是否有相同的SQL语句。
如果相同则可以减少最重要的解析工作(也就是生成执行计划),从而节省了大量的资源;反之,如果没有找到相同的SQL语句,则必须重新从头到尾进行完整的解析过程。
这部分存放SQL语句的内存就叫做共享池(shared pool)。当然,shared pool里不仅仅是SQL语句,还包括管理shared pool的内存结构以及执行计划、控制信息等等内存结构。

  当oracle在shared pool中查找相同的SQL语句的过程中,如果SQL语句使用了绑定变量(bind variable),那么就是比较SQL语句的静态部分,前面我们已经知道,静态部分是有限的,
很容易就能够缓存在内存里,从而找到相同的SQL语句的概率很高。如果没有使用绑定变量,则就是比较SQL语句的静态部分和动态部分,而动态部分的变化是无限的,
因此这样的SQL语句很难被缓存在shared pool里。毕竟内存是有限的,不可能把所有的动态部分都缓存在shared pool里,即便能够缓存,
管理这样一个无限大的shared pool也是不可能完成的任务。不使用绑定变量导致的直接结果就是,找到相同的SQL语句的概率很低,导致必须完整的解析SQL语句,也就导致消耗更多的资源。
从这里也可以看出,只有我们使用了绑定变量,才真正遵循了oracle引入shared pool的哲学思想,才能够更有效的利用shared pool.


SQL共享池的内存结构:
从一个物理的层面来看,shared pool是由许多内存块组成,这些内存块通常称为chunk.Chunk是shared pool中内存分配的最小单位,一个chunk中的所有内存都是连续的。
这些chunk可以分为四类,这四类可以从x$ksmsp(该视图中的每个行都表示shared pool里的一个chunk)的ksmchcls字段看到:
1) free:这种类型的chunk不包含有效的对象,可以不受限制的被分配。
2) recr:意味着recreatable,这种类型的chunks里包含的对象可以在需要的时候被临时移走,并且在需要的时候重新创建。
比如对于很多有关共享SQL语句的chunks就是recreatable的。
3) freeabl:这种类型的chunks包含的对象都是曾经被session使用过的,并且随后会被完全或部分释放的。这种类型的chunks不能临时从内存移走,
因为它们是在处理过程中间产生的,如果移走的话就无法被重建。
4) perm:意味着permanent,这种类型的chunks包含永久的对象,大型的permanent类型的chunks也可能含有可用空间,这部分可用空间可以在需要的时候释放回shared pool里。

  当chunk属于free类型的时候,它既不属于library cache,也不属于dictionary cache.如果该chunk被用于存放SQL游标时,则该chunk进入library cache;
同样,如果该chunk被用于存放数据字典的信息时,则该chunk进入dictionary cache.

  在shared pool里,可用的chunk(free类型)会被串起来成为可用链表(free lists)或者也可以叫做buckets(一个可用链表也就是一个bucket)。
我们可以使用下面的命令将shared pool的内容转储出来看看这些bucket.
  alter session set events 'immediate trace name heapdump level 2';

然后打开产生的转储文件,找到“FREE LISTS”部分,这是在9i下产生的bucket列表,9i以前的可用chunk的管理方式是不一样的。
我们可以看到,可用的chunk链表(也就是bucket)被分成了254个,每个bucket上挂的chunk的尺寸是不一样的,有一个递增的趋势。
我们可以看到,每个bucket都有一个size字段,这个size 就说明了该bucket上所能链接的可用chunk的大小尺寸。

  当一个进程需要shared pool里的一个chunk时,假设当前需要21个单位的空间,则该进程首先到符合所需空间大小的bucket(这里就是bucket 2)上去扫描,
以找到一个尺寸最合适的chunk,扫描持续到bucket的最末端,直到找到完全符合尺寸的chunk为止。
如果找到的chunk的尺寸比需要的尺寸要大,则该chunk就会被拆分成两个chunk,一个chunk被用来存放数据,
而另外一个则成为free类型的chunk,并被挂到当前该bucket上,也就是bucket 2上。
然而,如果该bucket上不含有任何需要尺寸的chunk,那么就从下一个非空的bucket上(这里就是bucket 3)获得一个最小的chunk.
如果在剩下的所有bucket上都找不到可用的chunk,则需要扫描已经使用的recreatable类型的chunk 链表,从该链表上释放一部分的chunk出来,
因为只有recreatable类型的chunk才是可以被临时移出内存的。
当某个chunk正在被使用时(可能是用户正在使用,也可能是使用了dbms_shared_pool包将对象钉在shared pool里),该chunk是不能被移出内存的。
比如某个SQL语句正在执行,那么该SQL语句所对应的游标对象是不能被移出内存的,该SQL语句所引用的表、索引等对象所占用的chunk也是不能被移出内存的。
当shared pool中无法找到足够大小的所需内存时,报ORA-4031错。当出现4031错的时候,你查询v$sgastat里可用的shared pool空间时,
可能会发现name为“free memory”的可用内存还足够大,但是为何还是会报4031错呢?事实上,在oracle发出4031错之前,已经释放了不少recreatable类型的chunk了,
因此会产生不少可用内存。但是这些可用chunk中,没有一个chunk是能够以连续的物理内存提供所需要的内存空间的,从而才会发出 4031的错。

  对bucket的扫描、管理、分配chunk等这些操作都是在shared pool latch的保护下进行的。如果shared pool含有数量巨大的非常小的free类型的chunk的话,
则扫描bucket时,shared pool latch会被锁定很长的时间,这也是8i以前的shared pool latch争用的主要原因。而如果增加shared pool尺寸的话,
仅仅是延缓shared pool latch的争用,而到最后,就会因为小的free chunks的数量越来越多,争用也会越来越严重。
而到了9i以后,由于大大增加了可用chunk链表(也就是bucket)的数量,同时,每个 bucket所管理的可用chunk的尺寸递增的幅度非常小,
于是就可以有效的将可用的chunk都均匀的分布在所有的bucket上。这样的结果就是每个bucket上所挂的free类型的chunk都不多,
所以在查找可用chunk而持有shared pool latch的时间也可以缩短很多。

对于非常大的对象,oracle会为它们单独从保留区域里分配空间,而不是从这个可用chunk链表中来分配空间。
这部分空间的大小尺寸就是由初始化参数 shared_pool_reserved_size决定的,缺省为shared_pool_size的5%,这块保留区域与正常的chunk的管理是完全分开的,
小的chunk不会进入这块保留区域,而这块保留区域的可用chunk也不会挂在bucket上。这块保留区域的使用情况可以从视图v$shared_pool_reserved中看到,
通常来说,该视图的request_misses字段显示了需要从保留区域的可用链表上上获得大的 chunk而不能获得的次数,该字段应该尽量为0.


说明:
(1)、SQL共享池包括数据字典缓冲区、库缓冲区、其他控制结构区。
(2)、其中库缓冲区又分为共享SQL区、PL/SQL过程与包区、锁与他控制结构区,当是共享服务器模式时还会多出私有SQL区、排序区。
(1)、其大小(字节为单位)由init.ora的SHARED_POOL_SIZE决定。默认80MB,可以在运行期间手动修改该参数,10g以后可以不用设定该参数,而只需要指定sga_target,
从而oracle 将自动决定shared pool的大小尺寸。
(2)、当数据块缓冲区和字典缓存区能够共享数据库用户间的结构及数据信息时,库缓存区允许共享常用的SQL语句。
(3)、包含执行计划及运行数据库的sql语句的语法分析树。在第二次运行(由任何用户)相同的sql语句时,可利用sql共享池中可用的语法分析信息来加快执行速度。
(4)、SQL共享池的置换规则由LRU算法管理,如果SQL共享池太小,将连续不断再装入到库缓存区,从而降低性能。
共享池中保存了最近执行的SQL语句、PL/SQL过程与包、数据字典信息、锁、以及其他控制结构的信息。
共享池是对SQL语句、PL/SQL程序进行语法分析、编译、执行的内存区。
认出的过程叫做解析SQL语句的过程,响应的过程叫做执行SQL语句的过程。解析的最终结果是要产生oracle自己内部的执行计划,从而指导SQL的执行过程。

3.1、字典缓存区(dictionary cache)
当数据库需要字典信息时,将读取数据字典表并将返回的数据存储在字典缓存区的SGA中。
字典缓冲区大小由数据库内部管理。置换规则由LRU算法管理。
如果字典缓冲区太小,数据库就不得不反复查询数据字典表以访问数据库所需的信息,这些查询称为循环调用,循环调用会导致查询速度更低。
用于存储经常使用的数据字典信息。比如(表的定义、用户名、口令、权限、数据库的结构等)。
Oracle运行过程中经常访问该缓存以便解析SQL语句,确定操作的对象是否存在,是否具有权限等。如果不在数据字典缓存中,服务器进程就从保存数据字典信息的
数据文件中将其读入到数据字典缓存中。数据字典缓存中保存的是一条一条的记录(就像是内存中的数据库),而其他缓存区中保存的是数据块信息。
dictionary cache则存放了在执行SQL语句过程中,所参照的数据字典的信息,包括SQL语句所涉及的表名、表的列、权限信息等。
dictionary cache也叫做row cache,因为这里面的信息都是以数据行的形式存放的,而不是以数据块的形式存放的。
对于dictionary cache来说,oracle倾向于将它们一直缓存在shared pool里,不会将它们交换出内存,因此我们不用对它们进行过多的关注。

3.2、库缓存区(library cache)
(大小与OPEN_CURSOR初始化参数相关,ORACLE中每条查询语句都需要打开一个游标,OPEN_CURSOR默认值为300)
库缓存的目的就是保存最近解析过的SQL语句、PL/SQL过程和包。
这样一来,Oracle在执行一条SQL语句、一段PL/SQL 过程和包之前,首先在“库缓存”中搜索,如果查到
它们已经解析过了,就利用“库缓存”中解析结果和执行计划来执行,而不必重新对它们进行解析,显著提高执行速度和工作效率。
ORACLE将每一条SQL语句分解为可共享、不可共享的两部分。
Library cache存放了最近执行的SQL语句、存储过程、函数、解析树以及执行计划等。
library cache是shared pool里最重要的部分,也是在shared pool中进进出出最活跃的部分,需要我们仔细研究。
所以,我们在说到shared pool实际上就可以认为是在指library cache.

当SQL语句(select object_id,object_name from sharedpool_test)进入library cache时,
oracle会到dictionary cache中去找与sharedpool_test表有关的数据字典信息,比如表名、表的列等,以及用户权限等信息。
如果发现dictionary cache中没有这些信息,则会将system表空间里的数据字典信息调入buffer cache内存,读取内存数据块里的数据字典内容,
然后将这些读取出来的数据字典内容按照行的形式放入dictionary cache里,从而构造出dc_tables之类的对象。
然后,再从dictionary cache中的行数据中取出有关的列信息放入library cache中。

  library cache最主要的功能就是存放用户提交的SQL语句、SQL语句相关的解析树(解析树也就是对SQL语句中所涉及到的所有对象的展现)、执行计划、
用户提交的PL/SQL程序块(包括匿名程序块、存储过程、包、函数等)以及它们转换后能够被oracle执行的代码等。为了对这些内存结构进行管理,
还存放了很多控制结构,包括lock、pin、dependency table等。

  library cache还存放了很多的数据库对象的信息,包括表、索引等等。有关这些数据库对象的信息都是从dictionary cache中获得的。
如果用户对library cache中的对象信息进行了修改,则这些修改会返回到dictionary cache中。

  在library cache中存放的所有的信息单元都叫做对象(object),这些对象可以分成两类:一类叫存储对象,也就是上面所说的数据库对象。
它们是通过显式的 SQL语句或PL/SQL程序创建出来的,如果要删除它们,也必须通过显示的SQL命令进行删除。这类对象包括表、视图、索引、包、函数等等;
另一类叫做过渡对象,也就是上面所说的用户提交的SQL语句或者提交的PL/SQL程序块等。这些过渡对象是在执行SQL语句或PL/SQL程序的过程中产生的,并缓存在内存里。
如果实例关闭则删除,或者由于内存不足而被交换出去,从而被删除。

  当用户提交SQL语句或PL/SQL程序块到oracle的shared pool以后,在library cache中生成的一个可执行的对象,这个对象就叫做游标(cursor)。
不要把这里的游标与标准SQL(ANSI SQL)的游标混淆起来了,标准SQL的游标是指返回多条记录的SQL形式,需要定义、打开、关闭。
下面所说到的游标如无特别说明,都是指library cache中的可执行的对象。游标是可以被所有进程共享的,也就是说如果100个进程都执行相同的SQL语句,
那么这100个进程都可以同时使用该SQL 语句所产生的游标,从而节省了内存。每个游标都是由library cache中的两个或多个对象所体现的,至少两个对象。
一个对象叫做父游标(parent cursor),包含游标的名称以及其他独立于提交用户的信息。从v$sqlarea视图里看到的都是有关父游标的信息;
另外一个或多个对象叫做子游标(child cursors),如果SQL文本相同,但是可能提交SQL语句的用户不同,或者用户提交的SQL语句所涉及到的对象为同名词等,都有可能生成不同的子游标。
因为这些SQL语句的文本虽然完全一样,但是上下文环境却不一样,因此这样的SQL语句不是一个可执行的对象,必须细化为多个子游标后才能够执行。
子游标含有执行计划或者PL/SQL对象的程序代码块等。

library cache的内部管理机制:

  oracle内部在实现管理的过程中大量用到了hash算法。hash算法是为了能够进行快速查找定位所使用一种技术。所谓hash算法,就是根据要查找的值,
对该值进行一定的hash算法后得出该值所在的索引号,然后进入到该值应该存在的一列数值列表(可以理解为一个二维数组)里,通过该索引号去找它应该属于哪一个列表。
然后再进入所确定的列表里,对其中所含有的值,进行一个一个的比较,从而找到该值。这样就避免了对整个数值列表进行扫描才能找到该值,
这种全扫描的方式显然要比hash查找方式低效很多。其中,每个索引号对应的数值列在oracle里都叫做一个hash bucket.

我们来列举一个最简单的hash算法。假设我们的数值列表最多可以有10个元素,也就是有10个hash buckets,每个元素最多可以包含20个数值。则对应的二维数组就是t[10][20].
我们可以定义hash算法为n MOD 10.通过这种算法,可以将所有进入的数据均匀放在10个hash bucket里面,hash bucket编号从0到9.比如,我们把1到100都通过这个hash函数均匀放到这10个hash bucket里,当查找32在哪里时,只要将32 MOD 10等于2,这样就知道可以到2号hash bucket里去找,也就是到t[2][20]里去找,2号hash bucket里有10个数值,逐个比较2号hash bucket里是否存在32就可以了。

  library cache就是使用多个hash bucket来管理的,其hash算法当然比我们前面列举的要复杂多了。每个hash bucket后面都串连着多个句柄(该句柄叫做library cache object handle),这些句柄描述了library cache里的对象的一些属性,包括名称、标记、指向对象所处的内存地址的指针等。可以用下图一来描述library cache的整体结构。
当一条SQL语句进入library cache的时候,先将SQL文本转化为对应ASCII数值,然后对该这些ASCII数值进行hash函数的运算,传入函数的是SQL语句的名称(name,对于SQL语句来说其name就是SQL语句的文本)以及命名空间(namespace,对于SQL语句来说是“SQL AREA”,表示共享游标。可以从视图v$librarycache里找到所有的namespace)。运用hash函数后得到一个值,该值就是hash bucket的号码,从而该SQL语句被分配到该号的hash bucket里去。实际上,hash bucket就是通过串连起来的对象句柄才体现出来的,它本身是一个逻辑上的概念,是一个逻辑组,而不像对象是一个具体的实体。oracle根据 shared_pool_size所指定的shared pool尺寸自动计算hash buckets的个数,shared pool越大,则可以挂载的对象句柄就越多。
  当某个进程需要处理某个对象时,比如处理一条新进入的SQL语句时,它会对该SQL语句应用hash函数算法,以决定其所在的hash bucket的编号,然后进入该hash bucket进行扫描并比较。有可能会发生该对象的句柄存在,但是句柄所指向的对象已经被交换出内存的情况出现。这时对应的对象必须被再次装载(reload)。也可能该对象的句柄都不存在,这时进程必须重新构建一个对象句柄挂到hash bucket上,然后再重新装载对象。SQL语句相关的对象有很多(最直观的就是SQL语句的文本),这些对象都存放在library cache里,它们都通过句柄来访问。可以把library cache理解为一本书,而SQL语句的对象就是书中的页,而句柄就是目录,通过目录可以快速定位到指定内容的页。
  对象句柄存放了对象的名称(name)、对象所属的命名空间(namespace)、有关对象的一些标记(比如对象是否为只读、为本地对象还是远程对象、是否被pin在内存中等等)以及有关对象的一些统计信息等。而且,对象句柄中还存放了当前正在lock住和pin住该对象的用户列表、以及当前正在等待 lock和pin该对象的用户列表。对象句柄中存放的最重要的内容就是指向Heap 0对象的指针了。Heap 0用来存放与对象有直接关系的一些信息,比如对象类型、对象相关的表(比如依赖表、子表等)、指向对象的其他数据块的指针(这些数据块指向了实际存放 SQL文本、PL/SQL代码、错误信息等的大内存块,这些大内存块依次叫做Heap 1、2、3、4等)等信息。
Heap是通过调用服务器进程进行分配的,任何对象都具有heap 0,至于还应该分配哪些其他的heap则是由对象的类型决定的,比如SQL游标具有heap 1和 6,而PL/SQL程序包则具有heap 1、2、3和4.按照heap的使用情况,oracle会在SGA(library cache)、PGA或UGA中分配heap,但是heap 0始终都是在library cache中进行分配的。如果所请求的heap已经在SGA中分配了,则不会在PGA中再次分配heap.Heap是由一个或多个chunk组成的,这些 chunk可以是分散的分布在library cache中的,不需要连续分布。
  如上图三中所看到的heap 0实际上是指heap 0的句柄,其中包含的对象包括:
1) object type:library cache中的对象类型包括:表、视图、索引、同名词等等。每个对象只能有一个object type,根据object type将对象归类到不同的namespace里。
一个object type对应一个namespace,但是一个namespace可能对应多个object type.这样的话,查找一个对象时,只要在该对象所属的namespace中去找就可以了。
比较常见的namespace包括:
  a) SQL AREA:也可以叫做CRSR,表示shared cursor,存放共享的SQL语句。
  b) TABLE/PROCEDURE:存放的object type包括:table、view、sequence、synonym、 procedure的定义、function的定义以及package的定义。
  c) BODY:存放procedure的实际代码、function的实际代码以及package的实际代码。
  d) TRIGGER:存放的object type为trigger.
  e) INDEX:存放的object type为index.
2) object name:对象名称由三部分组成:
  a) Schema的名称,对于共享游标(SQL语句或PL/SQL程序块)来说为空。
  b)对象名称。分为两种情况:对于共享游标(SQL语句或PL/SQL程序块)来说,其对象名称就是SQL的语句本身;而对于其他对象(比如表、视图、索引等)就是其在数据字典中的名称。
  c) Database link的名称。这是可选的,如果是本地对象,则为空。
  这样,对象的名称的格式为:SCHEMA.NAME@DBLINK.比如,可以为[email protected],也可以为hr.employees等。
3) flags:flags主要用来描述对象是否已经被锁定。对象具有三种类型的flag:
  a) public flag:表示对象上没有锁定(pin)或者latch.
  b) status flag:表示对象上存在锁定(pin),说明对象正在被创建或删除或修改等。
  c) specitial flag:表示对象上存在library cache latch.
4) tables:对每个对象,都会维护以下一串tables中的若干个:
  a) dependency table:含有当前对象所依赖的其他对象。比如一个视图可能会依赖其组成的多个表、一个存储过程可能依赖其中所调用的其他存储过程、一个游标可能依赖其中所涉及到的多个表等。Dependency table中的每个条目都指向一块物理内存,该物理内存中含有当前对象所依赖的对象的句柄。
  b) child table:含有当前对象的子对象,只有游标具有child table.Child table中的每个条目都指向一个可执行的SQL命令所对应的句柄。
  c) translation table:包含当前对象所引用的名称是如何解释为oracle底层对象的名称,只有游标具有translation table.
  d) authorization table:包含该对象上所对应的权限,一个条目对应一个权限。
  e) access table:对于dependency table中的每一个条目,都会在access table中存在对应的一个或多个条目。比如,假设对象A依赖对象B,那么在A的dependency table和access table中都会存在一个条目指向B.位于access table中的指向B的条目说明了对B具有什么样的访问类型,从而也就说明了用户要执行A则必须具有对B的权限。
  f) read-only dependency table:类似于dependency table,但是存放只读的对象。
  g) schema name table:包含authorization table中的条目所属的schema.
5) data blocks:对象的其他信息会存放在不同的heap中,为了找到这些heap,会在heap 0中存放多个(最多16个,但是这16个data block不会都用到)data blocks结构,每个data block含有指向这些实际heap内存块的指针。
  
除了heap 0以外,还有11个heap,根据对象的不同进行分配,并存放了不同的内容:
  1) Heap 1:存放PL/SQL对象的源代码。
  2) Heap 2:存放PL/SQL对象的解析树,这有个好听的名字: DIANA.
  3) Heap 3:存放PL/SQL对象的伪代码。
  4) Heap 4:存放PL/SQL对象的基于硬件的伪代码。
  5) Heap 5:存放了编译时的错误信息。
  6) Heap 6:存放了共享游标对象的SQL文本。
  7) Heap 7:可用空间。
  8) Heaps 8–11:根据对象的不同而使用的子heap.
  我们可以通过查询v$db_object_cache来显示library cache中有哪些对象被缓存,以及这些对象的大小
  尺寸。比如,我们可以用下面的SQL语句来显示每个namespace中,大小尺寸排在前3名的对象:
  select *
  from (select row_number() over(partition by namespace order by sharable_mem desc) size_rank,
  namespace,
  sharable_mem,
  substr(name, 1, 50) name
  from v$db_object_cache
  order by sharable_mem desc)
  where size_rank <= 3
  order by namespace, size_rank;


3.2.1、共享SQL区(shared SQL area)
存储的是最近执行的SQL语句、SQL解析后的语法树(parse tree)和优化后的执行计划(execution plan)。
这样以后执行相同的SQL语句就直接利用在共享SQL区中的缓存信息,不必重复语法解析了。
令多次运行的 SQL 语句使用同一个共享SQL区可以为Oracle节约大量的内存开销,这在大量用户运行相同应用的环境里尤为明显。
Oracle在执行一条新的SQL语句时,会为它在共享SQL区中分配空间,分配的大小取决于SQL语句的复杂度。
如果共享SQL区中没有空闲空间,就利用LRU算法,释放被占用的空间。

Oracle 处理各种 PL/SQL程序结构( program unit)(过程,函数,包,匿名块,及数据库触发器)的方式与处理单独的 SQL 语句类似。
Oracle 为每个程序结构分配一块公共内存区以保存其解析及编译的结果。同时 Oracle 还要为程序结构创建私有内存区。
当多个用户运行同一个程序结构时,所有用户都使用唯一的一个共享区,同时每个用户拥有一个私有区,存储此程序结构在用户会话内的独有信息。

3.2.2、PL/SQL过程和包区
工作原理同共享SQL区。但是嵌套在PL/SQL过程和包中的SQL语句、解析后的语法树和优化后的执行计划被存储中共享SQL区中。

3.2.3、锁与其他控制结构
存储ORACLE例程内部操作所需的信息。比如(各种锁、闩、寄存器值)。

3.2.4、私用SQL区(私有内存区)
存储在执行SQL语句、PL/SQL程序结构时,在其运行的会话中所独有的信息,每个会话或用户相关的私有信息。
包括本地变量(local variable),全局变量(global variable),
包变量(package variable)(也被称为包实例(package instantiation)),及SQL执行缓冲区(buffers for executing SQL)
其他会话即使执行相同的SQL语句也不会使用这些信息。比如(绑定变量、环境和会话参数)。

3.2.5、排序区
当是共享服务器模式时会出现的区。

4、大池
(1)、大池是一个可选内存区。如果使用线程服务器选项或频繁执行备份/恢复操作,只要创建一个大池就可以有效管理这些操作
(2)、大池将致力于支持sql大型命令。利用大池可以防止SQL大型命令把条目重写入SQL共享池中,从而减少再装入到库缓存区中的语句数量。
(3)、大小(字节为单位)由init.ora文件的large_pool_size参数设置,用户可以通过large_pool_min_alloc参数设置大池中的最小位置。
(4)、作为食用largepool的一种选择方案,可以用init.ora文件的shared_pool_reserved_size参数为sql大型语句保留一部分sql共享池。

(由初始化参数LARGE_POOL_SIZE确定大小,可以使用ALTER SYSTEM语句来动态改变大池的大小)
大池是可选项的,DBA可以根据实际业务需要来决定是否在SGA区中创建大池。如果没有创建大池,则需要大量内存空间的操作将占用共享池的内存。
ORACLE 需要打理内存的操作有:A、数据库备份和恢复。B、具有大量排序操作的SQL语句。C、并行化的数据库操作。

5、JAVA池(Java pool)
1)、JAVA池为用户存放JAVA代码、JAVA语句的语法分析表、JAVA语句的执行方案和进行JAVA程序开发。
大小(字节为单位)由init.ora文件的java_pool_size参数设置。默认为10MB。控制在30-50MB比较合适
2)、供各会话内运行的 Java 代码及 JVM 内的数据使用的。Java 池的内存使用方式与 Oracle 服务器的运行模式有关。
可以使用 ALTER SYSTEM SET JAVA_POOL_SIZE=0M SCOPE=SPFILE;语句在服务器初始化参数文件中修改该参数。必须重新启动数据库服务器才能使其生效。

6、多缓存池
(1)、可以在SGA中创建多个缓存池,能够用多个缓存池把大数据集与其他的应用程序分开,以减少它们争夺数据块存储区内相同资源的可能性。
(2)、对于创建的每一个缓冲池,都要规定其LRU锁存器的大小和数量,缓冲区的数量必须至少比LRU锁存器的数量多50倍。
(3)、创建缓存池时,需要规定保存区的大小和再循环区的大小。与SQL共享池的保留区一样,保存区保持条目,再循环区则被频繁地循环使用。
(4)、保存区大小可通过BUFFER_POOL_KEEP参数设定。保存和再循环缓冲池的容量减少了数据块缓冲存储区中的可用空间。
(5)、对于使用一个新缓冲池的表,通过表的storage子句中的buffer_pool参数来规定缓冲池的名字。
(6)、如果需要从内存中快速删除一个表,就把它赋予RECYCLE池。默认池叫DEFAULT,这样就能在以后用altertable命令把一个表转移到default池。

7、数据流池
在数据库中,管理员可以在 SGA 内配置一个被称为数据流池(Streams pool)的内存池供 Oracle 数据流(Stream)分配内存。


三、程序全局区(PGA)
说明:程序全局区包括会话区、堆栈区、游标状态区、当是专用服务器模式时还会多出排序区、私有SQL区。
程序全局区(PGA)是存储区的一个区域,由一个oracle用户进程所用,PGA中的内存不能共享。
但对于多线程服务器因其允许多个用户进程使用同一服务器进程,所以PGA的一部分和用户对话信息存储在SGA中,此时应增加SQL共享池。
PGA区是用户进程连接到数据库并创建一个对应的会话时,由ORACLE 为服务器进程分配的专门用于当前用户会话的内存区。该内存区是非共享、不可写的。
只有服务器进程本身才可以访问它本身的PGA区。而PGA是所有服务器进程都可以共享的、可写的内存区。PGA的大小由操作系统决定,其内存取决于专用
服务器还是共享服务器模式。会话结束时,ORACLE会自动释放PGA所占用的内存区。

1、会话区
存储会话所具有的权限、角色、性能统计信息。
会话内存(session memory)用于存储会话的变量(登录信息)及其他与会话有关的信息。对于共享服务器(shared server)而言,会话内存是共享的而非为某个会话所私有。

2、堆栈区
存储的是会话中的绑定便利、会话变量、SQL语句运行的内存结构等信息。

3、游标状态区
运行SQL语句或使用游标的语句时,ORACLE会在共享池中为该语句分配上下文区,游标实际是指向该上下文区的指针。在PGA区中的游标状态区存储的是会话
中当前使用的各个游标所处的状态。
Oracle 预编译程序(precompiler program)及 OCI 程序的开发者可以显式地打开游标(cursor)(游标即私有 SQL 区的句柄(handle)),
并在程序运行过程中依据游标名称使用相关资源。当 Oracle 执行某些 SQL 语句时隐式提交的递归游标(recursive cursor)还需使用共享 SQL 区(shared SQL area)。

4、排序区
用于存放排序操作产生的临时数据,排序区是影响PGA区大小的主要因素。排序区的大小由初始化参数SORT_AREA_SIZE 决定。而SORT_AREA_RETAINED_SIZE
参数是决定排序区操作结束后排序区保留的内存大小。从排序区释放的内存仍然属于服务器进程,并不返回给操作系统。
ORACLE利用内存比磁盘快的事实,将准备排序的数据先临时存储到排序区中,并在排序区中执行排序操作,然后将排序结果返回给用户。
排序原理:如果要排序的数据在排序区中放不下,则ORACLE就将数据分割成较小的块放在排序区中,然后对每一小块进行排序。排序产生的临时数据就先
放到临时表空间的临时段中。当每一小块都排序完成之后,再将这些排序完成的小块合并在一起,产生最终结果。

5、私有SQL区
私有 SQL 区(private SQL area)中包含绑定信息(bind information)及运行时内存结构(runtime memory structure)等数据。
每个提交了 SQL 语句的会话都有一个私有 SQL 区。每个提交了相同 SQL 语句的用户都有自己的私有 SQL 区,但她们使用同一个共享 SQL 区(shared SQL area)。
即多个私有 SQL 区可以和同一个共享 SQL 区相联系。

?软件代码区
于存储可运行的或正在运行的程序代码的内存空间。Oracle 系统程序的代码也存储在软件代码区,但其在此区域内的位置与用户程序完全不同,
系统程序代码所在的位置更独立,且保护更严密。
软件代码区的容量一般是固定的,只有软件升级或重新安装时才会改变。在不同的操作系统下,此区域所需的容量也有所不同。 软件代码区是只读的,
此区域既可以为共享的,也可以为非共享的。在某些情况下,Oracle 代码可以由所有用户共享,这避免了相同代码的重复复制。因而节约了内存的使用,
提高了系统的整体性能。
用户程序同样既可以为共享的,也可以为非共享的。有些 Oracle 工具(例如 Oracle Form 和 SQL*Plus)可以是共享的,但有些就不能共享。
当不同数据库的实例运行在同一计算机上时,她们可以使用同一个系统程序代码区。


你可能感兴趣的:(Oracle,体系结构,Oracle,内存结构)