【Oracle】体系结构
1. 理解实例和数据库
☞ 实例是一组后台进程和共享内存
☞ 数据库是磁盘上存储的数据集合
☞ 实例“一生”只能装载并打开一个数据库
☞ 数据库可以由一个或多个实例(RAC)装载和打开
[oracle@drz ~]$ ipcs -a
##显示进程间的通信设备,如共享内存、信号量等。
ORACLE_SID是Oracle的站点标识符,它是访问实例的键,设置命令如下:
[oracle@drz ~]$ export ORACLE_SID=drz
Oracle实例和数据库
Oracle中内存部分也称为系统全局区(SGA),它的主要作用是:
①维护所有进程需要访问的多种内部数据结构;
②缓存磁盘上的数据,另外重做数据写至磁盘之前先在这里缓存;
③保存以解析的SQL执行计划,等等。
2. 如何连接Oracle
2.1 专用服务器连接
☞ 专用服务器
常见的专用服务器配置
当有客户连接时,Oracle会为客户端创建一个新的进程,通常称为专用服务器配置,这个服务器进程会在客户端会话生存期中专门为客户端服务。会话与专用服务器之间是一对一映射关系。按照定义,专用服务器不是实例的一部分。客户进程会通过某种网络通道与这个专用服务器直接通信,并由这个服务器进程接收和执行SQL,必要的时候它还会读取数据文件,并在数据库的缓存中查找客户想要的数据。专用服务器进程的主要目标就是对客户提交的SQL调用作出响应。
2.2 共享服务器连接
☞ 共享服务器
当客户以共享服务器连接时,Oracle不会对每个客户的连接创建另外的线程或新的Unix进程。在Oracle7x和8x中,共享服务器叫做多线程服务器(MIS),现在已经弃用此名称。共享服务器实际上就是一种连接池机制。利用共享服务器,Oracle只需建立很少的一部分进程/线程,这些进程/线程将由所有会话共享。
共享服务器连接和专用服务器连接之间的最大区别是,与数据库连接的客户进程不会与共享服务器直接通信,因为这个服务器进程是共享的。为了共享这些进程,Oracle使用了一个或一组称为调度程序(dispatcher,也称为分派程序)的进程。客户进程通过网络与一个调度程序进程通信,这个调度程序将客户的请求放在SGA中的请求队列中。第一个空闲的共享服务器会得到这个请求,并进行处理。完成这个命令后,共享服务器会把响应放在原调度程序的响应队列中。调度程序进程一直在监听这个队列,发现这个结果后,就会把结果传给客户。整个流程如下图:
共享服务器请求的流程步骤
上图中,客户连接向调度程序发送一个请求。调度程序首先将这个请求放在SGA的请求队列①中。第一个空闲的共享服务器从请求队列中取出这个请求②并处理。共享服务器处理结束后再把响应放在相应队列③中,接下来调度程序拿到这个响应④,传回给客户。
3. TCP/IP连接基本原理
当我们使用TCP/IP连接时,会连接一个TNS服务名。TNS就是透明网络底层(Transparent Network Substrate),它是Oracle中处理客户远程连接的“基础”软件。TNS连接串告诉Oracle软件如何与远程数据库连接。一般在客户端都有一个tnsnames.ora文件,通常存放在$ORACLE_HOME/network/admin目录下。
当客户远程连接数据库时,服务器端会有一个监听器在tns指明的端口处监听连接请求,当监听器收到入站连接请求后,它会使用自己的配置文件检查这个请求,符合要求会被允许连接,不符合要求会被拒绝连接。
3.1 专用服务器远程连接
Unix上监听器进程和专用服务器连接
当我们发起专用服务器连接,监听器进程会为我们创建一个专用服务器。在Unix上,这是通过fork()和exec()系统调用做到的(在Unix中,要在初始化之后创建新进程,唯一的办法就是通过fork())。这个新的专用服务器进程继承了监听器建立的连接,现在就与数据库物理的连接上了。在Windows上,监听进程请求数据库进程为连接创建一个新线程。一旦创建了这个线程,客户就会“重定向”到该线程,然后就能建立物理连接了。
3.2 共享服务器远程连接
监听器进程知道实例中运行了哪些调度程序。接收到连接请求后,监听器会从可用的调度程序池中选择一个调度程序进程。监听器会向客户返回连接信息,其中说明了客户如何与调度程序进程连接,还可能把连接“转发”给调度程序进程(这由不同的操作系统和数据库版本决定)。监听器返回连接信息后,它的工作就结束了。然后客户直接与调度程序连接。
监听器与共享服务器连接
4. 内存结构
Oracle主要由三个内存结构:
系统全局区(System Global Area,SGA):这是一个很大的共享内存区域,几乎所有的Oracle进程都会访问这个区域。
进程全局区(Process Global Area,PGA):这是一个进程或者线程专用的内存,其他进程或者线程不能访问。
用户全局区(User Global Area,UGA):这个内存区与特定的会话相关联。当使用共享服务器时,UGA在SGA中分配;如果使用专用服务器,UGA在PGA中分配。
4.1 系统全局区
查询v$SGASTAT视图可以得到SGA内部内存分布:
select pool,name,bytes from v$sgastat order by pool,name;
SGA主要包括以下池:
①Java池(Java pool):Java池是为数据库中运行的JVM分配的一段固定大小的内存。在Oracle Database 10g中,Java池可以在数据库启动并运行时在线调整大小。
②大池(Large pool):共享服务器连接使用大池作为会话内存,并行执行特性使用大池作为消息缓冲区,另外RMAN备份也可以使用大池作为磁盘I/O缓冲区。大池可以在线调整大小。
③流池(Stream pool):这是Oracle流(Stream)专用的一个内存池,Oracle流是数据库中的一个数据共享工具。这个工具是Oracle Database 10g中新增的,可以在线调整大小。如果未配置流池,但是使用了流功能,Oracle会使用共享池中至多10%的空间作为流内存。
④共享池(Share pool):共享池包含共享游标(cursor)、存储过程、状态对象、字典缓存和诸如此类的大量其他数据。在Oracle Database 10g和9i中,共享池都可以在线调整大小。
⑤“空”池(Null pool):这个池其实没有名字。这是块缓冲区(缓存的数据库块)、重做日志缓冲区和“固定SGA”区专用的内存。
典型的SGA
在9i中,必须手动设置SGA的大小,10g中可以自动SGA内存管理,11g中可以自动内存管理。当采用自动内存管理时,只需设置MEMORY_TARGET值,数据库实例会自动的把SGA和PGA分配为最佳大小。
不论是自动内存管理还是手动内存管理,各个池的内存以颗粒(granule,也称为区纽)为单位进行分配。一个颗粒是大小为4M、8M或16M的内存区。颗粒是最小的分配单位,所以想要一个5M的Java池,而且颗粒大小是4M,Oracle实际上会为这个Java池分配8M.颗粒的大小由SGA的大小确定,可以查询v$sga_dynamic_components视图。
select component,granule_size from v$sga_dynamic_components;
4.1.1 固定SGA
固定SGA(fixed SGA)是SGA的一个组件,其大小因平台和版本而异。安装时,固定SGA会“编译到”Oracle二进制可执行文件本身中。在SGA中,有一组指向SGA中其他组件的变量,还有一些变量中包含了各个参数的值。固定SGA通常很小,大小不受控制。可以把这个区理解成SGA中的自启区,Oracle在内部要使用这个区来找到SGA的其他区。
4.1.2 重做缓冲区
如果数据需要写到在线重做日志中,则在写至磁盘之前要在重做缓冲区(redo buffer)中临时缓存这些数据。当满足一下条件时,缓冲区数据会被刷出:
①每三秒
②commit
③切换日志
④重做缓冲区1/3满
⑤重做日志数据缓存满1M
redo缓冲区的默认大小随操作系统和Oracle版本不同而不同,可以通过查询LOG_BUFFER这个参数来获得缓冲区的大小。
4.1.3 块缓冲区缓存
Oracle将数据库块写至磁盘之前,或者从磁盘读取数据库块之后,会把这些数据库块存储在块缓冲区缓存(block buffer cache)中。如果太小,查询会永远都运行不完;如果太大,其他程序的将没有运行空间。
Oracle8.0之前只有一个块缓冲区缓存。Oracle8.0之后,开始把SGA中各个段的以缓存块放在3个位置上。
①默认池(default pool):所有段块一般都在这个池中缓存。
②保持池(keep pool):一般来说,访问相当频繁的段会放在这个候选的缓冲区池中,如果把这些段放在默认缓冲区池中,尽管会频繁访问,但仍有可能因为其他段需要空间而被刷出。
③回收池(recycle pool):一般来说,访问很随机的大段可以放在这个候选的缓冲池中,这些块会导致过量的缓冲区刷新输出,而当你想要在用这个块时,它可能已经老化退出了缓存。要把这些段与默认池和保持池的段分开,这样就不会导致默认池和保持池中的块老化而退出缓存。
理论上讲,默认池中的对象应该足够热,可以保证一直呆在缓存中。
从Oracle9i开始,除了默认池、保持池和回收池外,还有第4种可选的缓存:db_Nk_caches。增加这些缓存是为了支持数据库中多种不同的块大小。在Oracle Database 9i之前,数据库中只有一种块大小(一般是2KB、4KB、8KB、16KB、32KB)。从Oralce Database 9i开始,数据库可以有一个默认的块大小,也就是默认池、保持池或回收池中存储的块的大小,还可以有最多4种非默认的块大小。
(1)在缓冲区缓存中管理块
每个池中的块管理方式是一样的。缓冲区缓存中的块实际上在一个位置管理,但有两个列表指向这些块:
①脏块(dirty)列表:其中的块需要由数据库块写入器(DBWn)写入磁盘。
②非脏(nondirty)块列表。
Oracle 8.0之前非脏块列表就是最近最少使用(Least Recently Used,LRU)列表。块按使用的顺序列出。在Oracle 8i及以后的版本中,不再按物理顺序来维护块列表,Oracle采用了一种接触计数(touch count)算法,如果命中缓存中的一个块,则会增加与之相关联的计数器。
管理这些列表的整个算法相当复杂,而且随着Oracle版本的变化也在变化,并不断改进。我们只要知道频繁使用的块会被缓存,不常使用的块不会缓存太久。
(2)多个块大小
每个不同的块大小都必须有其自己的缓冲区缓存。不同块大小的缓冲区缓存不共享。默认池、保持池和回收池只缓存具有默认大小的块。为了在数据库中使用非默认的块大小,需要配置一个缓冲区池来保存这些块。
例如:创建一个块大小为16KB的表空间
##首先创建16KB块缓冲区缓存
SYS@drz>alter system set db_16k_cache_size = 16m scope=spfile;
System altered.
SYS@drz>startup force;
ORACLE instance started.
Total System Global Area 835104768 bytes
Fixed Size 2257840 bytes
Variable Size 532679760 bytes
Database Buffers 293601280 bytes
Redo Buffers 6565888 bytes
Database mounted.
Database opened.
##创建16KB块的表空间
SYS@drz>create tablespace ts_16k datafile '/u01/app/oracle/oradata/drz/ts0
1.dbf' 2 size 16m
3 blocksize 16k;
Tablespace created.
4.1.4 共享池
共享池是Oracle缓存一些“程序”数据的地方。在解析查询时,解析得到的结果会缓存在这里。Oracle把系统参数存储在共享池中。数据字典缓存也存储在这里。
共享池的特点是有大量的小内存块,一般是4KB或更小,这样做的目的是为了减少内存的碎片问题。共享池中的内存根据LRU(最近很少使用)的原则来管理。可以使用DBMS_SHARED_POOL,来强制“盯住”共享池中的对象。
绑定变量对于提高共享池性能是很重要的。
4.1.5 大池
大池(Large pool):用于存放大块内存的分配。大池是一个回收型的空间。
大池专门用于以下情况:
①共享服务器连接:用于在SGA中分配UGA区。
②语句的并行执行:允许分配进程间的消息缓冲区,这些缓冲区用于协调并行查询服务器。
③备份:在某些情况下用于RMAN磁盘I/O缓冲区。
4.1.6 Java池
Java池(Java pool):在Oracle8.1.5中增加的,目的是支持在数据库中运行Java。如果用Java编写一个存储过程,Oracle会在处理代码时使用Java池的内存,参数为JAVA_POOL_SIZE。
因为专用服务器连接模式下,UGA在PGA中分配,共享服务器连接模式下,UGA在SGA中分配。所以专有服务器模式下的Java pool只包含每个Java类的共享部分,内存相对较小;当使用共享服务器模式时,Java pool包含以下部分:
① 每个Java类的共享部分;
② UGA 中用于各会话状态的部分。UGA余下的部分会正常的在共享池中分配,或者如果配置了大池,就会从大池中分配。
4.1.7 流池
流池(stream pool):是一个新的SGA结构,从Oracle 10g开始新增的。流(stream)这个概念是在Oracle 9iR2及以上版本中才有的。流是数据库的共享/复制工具。流池用于缓存流进程在数据库间移动或者复制数据时使用的队列消息。流使用的是内存中的队列,如果这些队列满了,最终还是会写出到磁盘。如果使用内存队列的Oracle实例由于某些原因失败了,就会从重做日志中重建这些内存中的队列。
在有流池特性的数据库版本中,如果没有配置流池,则流池会从共享池中分配最多10%的空间。
4.1.8 自动SGA内存管理
Oracle 10g开始 管理SGA内存也有两种方式:手动管理和自动管理。手动管理需要设置所有必要的池和缓存参数,自动管理则只需设置少数几个内存参数和一个SGA_TARGET参数。通过设置SGA_TARGET参数,实例就能设置各个SGA组件的大小以及调整他们的大小。
在Oracle 10g中,与内存相关的参数可以归为两类:
①自动调优的SGA参数:目前这些参数包括db_cache_size/shared_pool_size/large_pool_size和java_pool_size。
②手动SGA参数:这些参数包括log_buffer/streams_pool/db_nk_cache_size/db_keep_cache_size和db_recycle_cache_size。
在Oracle 10g及更高版本中,可以通过查询v$sgainfo来查询SGA的哪些组件大小可以调整。
SYS@drz>select * from v$sgainfo;
采用自动SGA内存管理时,确定自动调整组件大小的主要参数是SGA_TARGET,这个参数可以在数据库启动并运行时动态调整,最大可以达到SGA_MAX_SIZE参数值。
4.1.9 自动内存管理
从Oracle 11gR1开始,Oracle数据库开始使用自动内存管理。只需要设置memory_target,这个参数表示SGA和PGA的总量。数据库会根据工作负载自动调整二者大小。我们也可以设置SGA_TARGET和PGA_AGGREGATE_TARGET,为各个内存区的大小设定下界,可以通过修改转储参数文件spfile来实现修改。
4.2 进程全局区和用户全局区
PGA(进程全局区)是特定于进程的一段内存,也就是一个操作系统进程或线程专用的内存,不允许系统中的其他进程和线程访问。PGA一般通过C语言的运行时调用malloc()或memmap()来分配,而且可以在运行时动态扩大。PGA不会从SGA中分配,而是通过进程或者线程在本地分配。PGA中的P代表Process,是不共享的。
UGA(用户全局区)是会话访问的的一块内存。当使用共享服务器连接时,UGA需要存储在每个共享服务器进程都能访问的一个内存结构中,也就是SGA中。当使用专用服务器连接时,不再需要所有会话都能访问你的会话状态,UGA从PGA中分配。
PGA包含进程内存,还可能包含UGA。PGA内存中的其他区通常用于完成内存中的排序、位图合并以及散列。
从Oracle 9i起,有两种方式来管理PGA中的非UGA内存:
①手动PGA内存管理
②自动PGA内存管理
从Oracle 11g开始,自动PGA内存管理可以用以下两种方式:
①通过设置PGA_AGGREGATE_TARGET初始化参数
②通过设置MEMORY_TARGET初始化参数
4.2.1 手动PGA内存管理
① SORT_AREA_SIZE:在信息换出到磁盘之前,用于对信息排序的RAM总量。
② SORT_AREA_RETAINED_SIZE:排序完成后用于保存已经排序的数据的内存总量。也就是说,如果SORT_AREA_SIZE是512KB,SORT_AREA_RETAINED_SIZE是256KB,那么服务器进程最初处理查询时会用512KB的内存对数据排序。等到排序完成后,排序区会“收缩”为256KB,这256KB内存中放不下的已排序数据会写出到临时表空间中。
③HASH_AREA_SIZE:服务器进程在内存中存储散列表所用的内存量。散列联结时会使用这些散列表结构,通常把一个大集合与另一个集合联结时就会用到这些结构。两个集合中较小的一个会散列到内存中,散列区中放不下的部分都会通过联结键存储在临时表空间中。
SORT_AREA_SIZE减掉SORT_AREA_RETAINED_SIZE所得的这部分内存一般从PGA中分配,SORT_AREA_RETAINED_SIZE这部分内存会在UGA中分配。通过查询动态性能视图(dynamic performance view)可以查看PGA和UGA内存的当前使用情况。
4.2.2 自动PGA内存管理
自动PGA内存管理不再使用SORT_AREA_SIZE、BITMAP_MERGE_AREA_SIZE和HASH_AREA_SIZE这些参数,引入自动PGA内存管理是为了解决一下问题:
① 易用性。
②自动管理可以根据工作负载动态地调整实际使用的内存量。
③内存控制。
4.2.3 如何设置自动PGA内存管理
需要调整一下两个参数:
① WORKAREA_SIZE_POLICY: 这个参数可以设置为MANUAL或者AUTO,如果是MANUAL,那么我们要使用排序区和散列区大小参数来控制分配的内存量;如果是AUTO,分配的内存量会根据数据库中的当前工作负载而变化。默认值是AUTO。
② PGA_AGGREGATE_TARGET: 这个参数会控制实例为完成数据排序和散列的所有工作区(排序区和散列区)总共应分配多少内存。在不同的版本中,这个参数的默认值有所不同,可以使用多种工具来设置,如DBCA。一般来说,如果使用自动PGA内存管理,就应该显式的设置这个参数。