Oracle数据库管理与维护实战
上QQ阅读APP看书,第一时间看更新

第2章 Oracle体系结构及其基本概念

第1章介绍了Oracle产品结构,目的是让初学者对Oracle有一些宏观的了解。Oracle是个庞大的体系,结构复杂,内容涉及内存、外存及网络。本章的目的就是让读者了解Oracle的体系结构,知道Oracle的运行机制,为后面的数据库管理打好基础。

2.1 Oracle总体结构

总体来说,Oracle服务器由实例(Instance)和数据库(Database)两大部分组成,如图2-1所示。实例由内存和一组进程组成。数据库则由一系列物理文件组成,这些物理文件是数据库的载体(注意和通常意义上的数据库区分)。用户通过实例访问数据库。一般情况下,一个实例对应一个数据库,也有例外的情况,例如在实时应用集群的并行系统中,一个数据库则对应多个实例。

图2-1 Oracle总体结构

2.1.1 Oracle实例

实例又叫数据库服务器,由SGA(System Global Area,系统全局区)和一组后台进程组成,SGA和这组后台进程被所有用户共享。数据库实例在数据库启动时建立,建立时给数据库分配共享内存区SGA和一系列后台进程。

SGA用于存放最近常从数据库中查询的数据和一些数据库控制信息。SGA主要包括共享池、数据缓冲区和日志缓冲区,如图2-2所示。

图2-2 Oracle实例

根据操作系统中的定义,进程是指在系统中能独立运行并作为资源分配的基本单位。Oracle中的后台进程可以看作是操作者、操作内存和磁盘I/O等资源。后台进程同样也是操作系统进程。

Oracle后台进程由一组进程组成。这组进程主要来操作数据库,管理并控制数据库的状态。后台进程包括PMON(进程监控程序)、SMON(系统监控程序)、DBWR(数据库写入程序)、LGWR(日志写进程)、CKPT(检查点进程)、RECO(数据恢复进程)、ARCH(归档进程)等,除ARCH外,前6个进程都是必须使用的进程。

实例是Oracle服务器的核心,数据库的存取和控制都由它来完成。

2.1.2 数据库

数据库即存在磁盘上的物理文件,有数据文件、控制文件和日志文件三类,数据库安装完后通过文件系统可访问到这些物理文件。这些物理文件是数据库的载体,数据库中的所有数据,包括系统数据都存储在这些物理文件中。后面的章节将会详细讲解如何使用、保护这些物理文件。

2.1.3 Oracle相关的几个概念

Oracle的相关资料中,经常会看到事务、DML、DDL等几个概念。这几个概念不易理解,本书虽然尽量避免使用这几个概念,但还是会使用一些。为方便读者阅读,本节给出这几个概念的定义。

1.事务

事务是从英文Transaction翻译过来的,事务的Oracle官方解释是:事务是包含一个或多个SQL语句的逻辑工作单元。事务中的SQL语句要么全部提交(Commit),要么全部回滚(Rollback)。

从上面的解释看,事务有点类似于操作系统中的进程,执行时不可分割,要么一起提交,要么一起回滚,我们可以将事务理解为与Oracle服务器的一次交互。Oracle中有多种事务类型,包括读写事务、只读事务、离散事务,默认的就是读写事务。

❑ 读写事务:指事务可包含查询、插入、更新与删除表记录的SQL语句,即包含更新数据的语句和查询数据的语句。

❑ 只读事务:顾名思义,事务中不能包含更新数据的语句,只能对数据进行读操作,不能进行写操作。

❑ 离散事务:离散事务主要用来改进数据库性能,在离散事务中将一些耗时的操作集中起来一起执行。

我们可以用“SET TRANSACTION <事务类型>”命令设置事务类型。

2.DML语句

数据库中将SQL语句分成三类:DML语句、DDL语句、DCL语句。DML语句称为数据库维护语言(Data Manipulation Language或者Data Modification Language)。

它包括SELECT、INSERT、UPDATE、DELETE语句,后三个语句会更新表数据,用于实现对表数据的操作。

3.DDL语句

数据定义语言(Data Definition Language,DDL),指一些创建、修改、删除数据库对象的语言,相应的语句有CREATE、ALTER和DROP。

4.DCL语句

数据库控制语言(Data Control Language,DCL),用于控制用户对数据库的访问。常见的三条命令是GRANT、REVOKE、SET ROLE。

我们将常见的SQL命令归纳一下,如表2-1所示。

表2-1 常见的SQL命令

2.1.4 Oracle的结构划分

为更好地理解和管理Oracle,从不同的角度,Oracle服务器结构被分为内部存储结构、进程结构、物理结构以及逻辑结构,本书将从这四部分来介绍数据库管理,下面详细介绍各结构。

2.2 Oracle内存结构

Oracle内存存储了数据字典信息(即关于对象、逻辑结构、模式、权限等等的元数据)、缓冲的应用数据、SQL语言、PL/SQL和Java程序数据,以及事物、控制、用户请求信息。图2-3是Oracle内存结构图,Oracle内存主要由SGA(系统全局区,System Global Area)和PGA(程序全局区,Program Global Area)两个区组成,此外还有重做日志缓冲区、大池、Java池等。

图2-3 Oracle内存结构

2.2.1 系统全局区SGA

SGA是数据库所有用户共享的存储区,它在实例启动时分配,实例关闭时回收。从Oracle 9i起,系统可动态分配SGA大小,即在数据库运行期间可改变SGA中各缓冲的大小,并且及时生效,而Oracle 9i之前版本的数据库,SGA的参数更改后必须将数据库重新启动才生效。在动态调整SGA各缓冲大小时,要注意的是必须低于初始化参数表SGA_MAX_SIZE的值。

SGA的大小对数据库性能有重大影响,实例调整主要是对SGA内各缓冲大小的调整。

1.SGA的自动内存管理

Oracle 10g之前版本的数据库,数据库管理员需要手动配置与SGA有关的大量初始化参数,有SHARED_POOL_SIZE、DB_CACHE_SIZE、JAVA_POOL_SIZE和LARGE_POOL_SIZE等。而从Oracle 10g数据库起,则使用自动数据库管理,数据库管理员只需配置SGA内存总量,即只需配置初始化参数:SGA_TARGET,数据库会自动给SGA内各区分配大小。

自动分配SGA内各区大小时,各区的大小能自动随需求变动。举一个例子,如果总共有1GB大小的内存供SGA使用,并且设置如下初始化参数:

        SHARED_POOL_SIZE = 128M
        DB_CACHE_SIZE   = 896M

如果用SGA自动管理,将SGA_TARGET大小设置成1GB,当一个应用程序需要更大的SHARED_POOL_SIZE共享池,Oracle可以从DB_CACHE_SIZE缓冲区中调整相应的内存给SHARED_POOL_SIZE。仅设置一个SGA总量有一个优势,就是很少会发生内存溢出。

SGA内的参数可以自动调整大小,如共享池、Java池、大池、数据库块缓冲区、Streams池。如果不使用这些缓冲区,这些缓冲区的大小就为0;而一旦要使用某个缓冲区,Oracle就会自动给这部分分配一个合理的大小而不需要人工干预。对用户来说,这些缓冲的调整和分配是透明的。

数据库实例一直会监视SGA内存的使用。实例会用内部视图和一些统计数据来决定如何给各组件最优分配内存。当SGA内存需求变化时,数据库会根据一定的算法,考虑当前和长远的需求来重新分配内存。

但SGA区中还有一部分内存区必需手动调整,这些区有Keep/Recycle缓冲区,相应的初始化参数为DB_KEEP_CACHE_SIZE和DB_RECYCLE_CACHE_SIZE。还有非标准数据块参数,由参数DB_nK_CACHE_SIZE,n = {2,4,8,16,32}设定。

2.2.2 数据库缓冲区

数据库缓冲区是SGA中的一个高速缓冲区域,用来存储最近从数据文件中读出来的数据块,如表、索引数据块。数据库缓冲区是Oracle提高访问速度的一种有效方法。SGA是所有用户共享的。处理查询时,服务器进程会先从数据库缓冲区查找所需数据块,只有缓冲区中没有时才会访问磁盘。

数据库缓冲区对数据库性能有很大影响。在Oracle 10g数据库之前,数据库缓冲区的大小由DB_BLOCK_BUFFERS参数决定。这个参数可在数据库服务器中的初始化参数文件中设置。数据库性能调优时,调整DB_BLOCK_BUFFERS大小是很重要的一部分。参数DB_BLOCK_SIZE的大小由DB_BLOCK_SIZE和DB_BLOCK_BUFFER相乘得到。DB_BLOCK_SIZE是指缓冲区中可放的物理块的大小,即数据库一次I/O访问的大小。从Oracle 10g数据库后,数据库缓冲区大小由Oracle自动调整。

数据库缓冲区有三种类型:Dirty Buffer、Pinned Buffers、Free Buffer。Dirty Buffer是已被修改需要写回磁盘的数据块,Pinned Buffers是正被访问的数据块,Free Buffer是闲空数据块。

Oracle为了便于管理缓冲区,将缓冲区数据块连成两个队列:写队列和LRU队列。写队列中的数据块排队等待被写回磁盘。Oracle修改了数据之后,并不立即将数据写回磁盘,而是累积到一定数量后才写回。因为访问磁盘I/O速度较慢,如果数据块一被修改就写回到磁盘,会导致频繁访问I/O,造成I/O瓶颈,影响系统性能;而用队列后就可以将多个数据块内容一次性写回磁盘,节省I/O资源。

LRU队列又称为最近最少使用队列,由Free Buffers、Pinned Buffers以及没有加入到写队列的Dirty Buffer组成。所谓最近最少使用队列就是将经常使用的数据块放在队首,将最不经常使用的放在队尾,当有新的数据块要加入时,则最先淘汰最不经常使用的队尾数据块。

当磁盘数据块写入缓冲时,进程首先会在LRU队列搜索Free Buffers,从LRU队尾开始搜索,搜索到足够数量的Free Buffers后,将数据从磁盘读到这些Free Buffers内,将这些Free Buffers放在LRU最不经常使用的队尾。在搜索Free Buffers过程中如果发现队列中有Dirty Buffer,进程就顺便将这Dirty Buffer放入写队列。在搜索Free Buffers的过程中,进程并不会遍历整个LRU队列,而是在搜索了一定数量的数据块后如果仍然没有搜索到足够的Free Buffers块,就停止搜索。这时用户进程会通知DBWR(数据库写进程)将Dirty Buffer数据写回磁盘,以释放空间。

Oracle将数据库缓冲区划成三个区域:KEEP、RECYCLE、DEFAULT。KEEP区中的内容能长期驻留,数据块不会被淘汰出去。这个区域的大小由BUFFER_POOL_KEEP决定。RECYCLE区中存储很少使用的数据,该区的内存可直接回收,其大小由BUFFER_POOL_RECYCLE决定,剩余的缓冲都属于DEFAULT区。

2.2.3 重做日志缓冲区

用户通过INSERT、UPDATE、DELETE、CREATE、ALTER、DROP等SQL命令更改了数据库后,服务器进程会将这些修改记录到重做日志缓冲区内,这些修改记录也叫重做记录(entry)。数据库发生崩溃后,用户可从重做日志缓冲区读取修改记录恢复数据库。重做日志缓冲区记录了发生修改的块、修改的位置,以及修改后的新值。注意,重做记录只记录每一个修改,并不记录发生修改的块类型,所以不能区分修改是在数据块上还是在回滚或索引段上。

重做日志缓冲区是一个循环缓冲区,缓冲区在被覆盖之前会由后台进程LGWR(日志写入进程)将缓冲内容写入联机重做日志文件,所以Oracle数据库对数据库的每次更改都有记录。重做日志缓冲区的大小由初始参数LOG_BUFFER决定。

2.2.4 共享池

共享池包括库高速缓冲(Library Cache)和数据字典缓冲(Data Dictionary Cache),如图2-3所示。库高速缓冲区又包括共享SQL区、私有SQL区(只在共享服务器内有)、共享PL/SQL区,以及控制结构区。从Oracle 10g开始,数据库能够自动调整共享池大小。

1.共享SQL区

Oracle将执行过的SQL分成两部分:共享SQL区和私有SQL区。当用户执行SQL语句时,Oracle会将最近执行过的SQL语句的文本、编译后的语法分析树和执行计划(指要完成一条SQL语句,Oracle服务器所需具体实施的步骤)存入共享SQL区,而将SQL语句中的变量值存入私有SQL区(在共享服务器中存入私有SQL区,在专用服务器中存入PGA内)。当服务器再次执行相同SQL语句时,服务器进程将不再对这条语句执行语法分析,而是直接执行SQL共享区中已存在的内容。这种方式有利于提高数据库性能。

注意,这里的相同是指语句完全相同,即语句结构相同,大小写相同,变量值相同,否则Oracle服务器会认为不同语句。

2.私有SQL区

私有SQL区中存放的是SQL语句执行时与每一个会话有关的私有数据,如连接变量的值。专用服务器内私有SQL区存在PGA中;共享服务器内私有SQL区存在共享池中。

3.共享PL/SQL区

Oracle处理PL/SQL程序与处理SQL语句的方法相同。执行一个PL/SQL程序单元时,Oracle将程序单元放入共享PL/SQL区,而将程序单元内的SQL语句放到共享SQL区中;当再次需要执行相同的程序单元时,就直接从内存中调用,而不需访问磁盘。

4.控制结构区

这是供实例内部使用的一段内存区,存放了锁、锁存器等方面的信息。

2.2.5 数据字典缓冲区

数据字典缓冲区是共享池的一部分,又称为字典区或行缓冲区,它包含了数据库的结构、用户信息和数据库的表、视图等信息。数据字典缓冲区存储了数据库里所有表和视图的名字、数据库基表的列名和列数据类型以及所有Oracle用户的权限等。

Oracle在分析SQL语句时会频繁地访问数据字典。由于Oracle的频繁访问,内存中专门设置了这个存储区存放数据字典。数据字典缓冲区由Oracle的所有用户进程共享。

数据字典缓冲区同样采用LRU算法来管理,缓冲区大小由数据库内部管理。数据字典缓冲区包含在SGA的共享池内,因此它的大小受共享池大小的限制。

字典缓冲区对数据库性能影响很大。在执行SQL语句过程中,服务器进程会经常访问数据字典缓冲区。如果缓冲区太小,数据库就会因为在缓冲中找不到所需信息而反复通过I/O从磁盘获取,严重影响系统性能。

2.2.6 程序全局区PGA

PGA(程序全局区,Program Global Area)包括会话信息、堆栈空间、排序区,以及游标状态。会话信息存放的是会话的权限、角色和会话性能统计等信息,堆栈空间内存放的是变量、数组和属于会话的其他信息,排序区则是用于排序的一段专用空间,游标状态存放的是当前使用的各种游标的处理阶段。

用户进程连接到Oracle数据后,服务器创建会话,同时分配一个PGA区。一个PGA区由一个Oracle用户进程所使用,不能共享。对专用服务器(一个数据库连接对应一个专用服务器进程),PGA保存堆栈空间信息和会话信息、游标状态、排序区。对共享服务器,PGA只保存堆栈空间信息,会话信息、游标状态、排序区保存在SGA中。PGA的结构如图2-4所示。

图2-4 PGA结构

初始化参数PGA_AGGREGATE_TARGET可以改变PGA的大小。PGA_AGGREGATE_TARGET是指所有服务器进程PGA占用内存的大小之和,它随服务器进程一起产生和释放。

2.2.7 排序区

排序区(Sort Area)是给所要排序的SQL语句提供的专用内存空间。一个排序区使用的例子如图2-5所示,如执行语句SELECT ENAME,SAL FROM EMP,不需要排序,进程DBWR先将数据从磁盘读到数据库缓冲区,再将数据传给用户进程。而执行SELECT ENAME,SAL FROM EMP ORDER BY SAL时,因为要以SAL排序,所以数据从磁盘读到数据库缓冲后,先以SAL排序,然后将排序结果存入排序区,再将排序结果返回给用户进程。

图2-5 排序结果

如果内存不够,Oracle会将数据分割成很多小块放到排序区内,然后对每一小块分别进行排序。排序过程中Oracle将排序区存放不下的数据都存放到磁盘临时段中,最后将这些小段合并起来返回给用户进程。在排序过程中,排序区的大小是动态变化的,但最大不能超过参数SORT_AREA_SIZE的值。

还有一个与排序有关的参数SORT_AREA_RETAINED_SIZE,它决定了排序完成后排序区占用内存的大小。排序区的内存释放后仍然属于服务器进程,并没释放给操作系统。为了提高排序速度,用户应适当调节好SORT_AREA_SIZE大小,使排序尽量在内存中进行。

2.2.8 软件代码区

软件代码区用于存储Oracle系统程序和用户程序正在执行或可以执行的程序代码。软件代码区是只读的,可安装成共享或非共享两种形式。Oracle系统程序是可共享的,用以使多个Oracle用户可存取。用户程序可以设为共享,也可以设为不共享。Oracle系统程序与用户程序不同,前者存放在软件代码区中较为安全的地方。

软件代码区的大小一般是固定的,只有在软件进行修改或重新安装时才能由操作系统改变大小。在支持多例程Oracle中,同一台机器上运行的几个数据库可以使用同一个代码区。

2.2.9 大池

大池(Large Pool)是一个可选内存区,从Oracle 9i版本数据库开始,大池大小由参数LARGE_POOL_SIZE决定;但Oracle 10g版本数据库以后,Oracle自动调整大池大小。在以下几种情况下我们要使用大池:使用共享服务器、执行备份和恢复操作、使用I/O Slaves(要配置DBWR_IO_SLAVES参数)。大池主要为一些需要消耗大量内存的操作提供更大内存空间。

2.2.10 Java池

Java池内存储了Java语句的文本、语法分析表等信息。Java池为Java的命令提供语法分析,从Oracle 9i版本数据库开始,Java池大小由参数JAVA_POOL_SIZE决定;但Oracle 10g版本数据库以后,Oracle自动调整大小。如果要安装Java VM,用户就必须启用Java池。

2.2.11 Streams池

Streams池的主要功能是存放消息(message)。池中存放的消息是共享的,Streams的信息可以从一个数据库传播到另一个数据库。利用Streams池管理消息比原来捕获和管理消息更容易。

用户可以在SGA中分配Streams池。Streams池大小通过初始化参数STREAMS_POOL_SIZE分配。如果没有设置这个参数,Oracle在Streams第一次使用时会自动创建Streams池。

2.3 Oracle进程

Oracle数据库中有许多进程,为了便于用户掌握,Oracle中将进程分为3种类型,分别为用户进程、服务器进程、后台进程,如图2-6所示。

图2-6 用户进程和服务器进程

2.3.1 用户进程

当用户连接数据库时,就会产生用户进程。如果要理解用户进程,我们必须掌握该类型进程涉及的两个概念,连接和会话。

1.连接

用户要与服务器进行交互,首先要建立起连接。连接是用户进程与服务器进程之间的通信通道。如果用户进程与服务器进程在同一服务器上,它们就通过服务器上的内部进程通信机制建立通信通道;如果用户进程在一个客户端,就通过网络建立通信通道。

2.会话

会话是用户与服务器之间连接的一种保持机制。当服务器确认用户身份后,就开始两者间的会话。用户进程有以下几个特点。

❑ 用户进程运行在客户机上。

❑ 用户进程与服务器进程在同一台机器上,用户进程与服务器可以合在一起,以节省系统资源。

❑ 在专用服务器配置中,用户向服务器发送一个服务请求,服务器就相应建立一个服务器进程。

2.3.2 服务器进程

用户进程不能与数据库实例直接交互,而是通过服务器进程与数据库实例交互,服务器进程有以下特点。

❑ 运行在服务器上,对专用服务器,一个用户请求建立一个服务器进程。在专用服务器上,服务器进程由用户进程激发,随用户进程的产生而产生,随用户进程的断开而消失。

❑ 对共享服务器,有可能多个用户进程共享一个服务器进程。

❑ 每一个服务器进程占用一个PGA。

服务器进程的作用有以下几点。

❑ 服务器进程对应用所发出的SQL语句进行语法分析和执行。

❑ 服务器进程从磁盘(数据文件)中读入必要的数据块到SGA的共享数据库缓冲区(该块不在缓冲区时)。

❑ 服务器进程将结果返回给应用程序处理。

2.3.3 专用服务器和共享服务器

服务器有两种配置方式,专用服务器配置和共享服务器配置。对不同的要求,用户可能以不同的方式连接数据库。

1.专用服务器配置

专用服务器进程中,一个用户请求对应一个服务器进程,如图2-7 所示。专用服务器中的用户请求处理过程如下。

图2-7 专用服务器与用户进程

(1)客户机启动用户进程。

(2)客户机通过Oracle Net将请求发送给服务器,服务器的监听器检测到请求。

(3)服务器验证用户发来的用户名和口令。

(4)验证用户名和口令正确后,用户发送SQL语句。

(5)专用服务器进程确定共享SQL区是否有同样的SQL,如果没有,则为SQL分配新的共享SQL区,同时进行语法分析、编译,生成执行计划,创建PGA存放当前进程的私有信息。

(6)专用服务器进程从数据库缓冲区中读数据块,如果没有所需数据块,就从磁盘读取。

(7)专用服务器进程执行SQL语句。

(8)专用服务器进程将结果返回给用户进程。

2.共享服务器配置

如果很多用户连接到专用服务器,会产生大量的服务器进程。大部分时间这些服务器进程是空闲的,这就造成大量资源的浪费。共享服务器就是使一个服务器进程管理多个用户进程,以降低系统开销,增加用户个数。

共享服务器进程和用户进程不是一一对应的。共享服务器进程和专用服务器进程提供相同的功能,但共享服务器进程的PGA不包含与用户相关的数据,它仅包含栈空间。所有与用户有关的信息都包含在SGA中。每一个共享服务器进程可以存取全部会话的数据空间,从而使任何服务进程都可以处理任何会话的请求。

共享服务器配置中,实例启动时会启动一定数量的服务器进程,这些服务器进程以循环方式处理用户进程的请求。共享服务器用“调度进程”(分配器)管理用户请求。如图2-8所示,用户请求时,先加入请求队列。调度进程从请求队列中取出用户请求交给可用的共享服务器进程处理,处理完后将请求放入响应队列。Oracle可以根据请求队列的长度动态地调整共享服务器进程。

图2-8 用户进程与共享服务器进程

共享服务器处理用户请求的过程如下。

(1)服务器启动实例时也启动多个共享服务器进程和一个调度进程,同时在SGA中创建请求队列和响应队列。

(2)客户机产生用户进程并用Oracle Net将请求通知服务器。服务器Oracle Net监听检测到请求后,识别请求使用的协议,然后将用户进程连接到该协议的调度进程。如果用户请求的协议没有可用的分配器,服务器就启动专用服务器进程处理请求。

(3)用户身份确认后用户发出SQL语句,服务器调度进程分析请求,把请求和调度进程的ID一起放入到请求队列队尾。

(4)共享服务器进程确定共享SQL区是否有同样的SQL,如果没有,则为SQL分配新的共享SQL区,同时进行语法分析、编译,生成执行计划,创建PGA存放当前进程的私有信息。

(5)共享服务器进程从数据库缓冲区中读数据块,如果没有所需数据块,就从磁盘读取。

(6)共享服务器进程执行SQL语句。

(7)请求的结果放到调度进程的响应队列。

(8)调度进程定时检查响应队列,如果发现有响应就将响应发回用户请求进程。

需要注意的是,在共享服务器配置的服务器中,可以同时用共享服务器进程和专用服务器进程。

2.4 Oracle后台进程结构

后台进程存在于操作系统中,和实例同时启动,由Oracle服务器管理。后台进程维持数据库的物理结构和内存结构,如图2-9所示。

图2-9 Oracle后台进程

后台进程运行时会创建一个跟踪文件,用以保存实例的操作。后台进程跟踪文件的命名方法和位置随操作系统和数据库版本的不同而不同。一般来说,跟踪文件含有后台进程名或后台进程的操作系统进程ID。用户可以通过设置初始化参数文件的BACKGROUND_DUMP_DEST参数来规定后台进程跟踪文件的位置,但是有些版本的Oracle忽略这种设置。

排除数据库故障时,跟踪文件就显得非常重要。影响后台进程的严重问题通常记录在数据库的跟踪文件上。跟踪文件位于BACKGROUND_DUMP_DEST目录下。这个目录默认是%ORACLE_BASE%/admin/<实例名称>/bdump目录。

2.4.1 SMON系统监控进程

在数据库启动时,SMON系统监视进程会使用联机重做日志文件恢复崩溃的实例。实例启动后,SMON还会清除不经常使用的临时段、合并表空间中连续的自由空间。在并行数据库服务器环境下,SMON对有故障的CPU或实例进行实例恢复。

Oracle会定期唤醒SMON,以检查系统是否需要它。其他进程也可以调用SMON系统监控进程。

2.4.2 PMON进程监控进程

PMON进程主要用来监视用户进程的运行,当用户进程失败时,清除失败的用户进程和失败进程所占用的资源。PMON能清除数据库缓冲区的数据,断开异常中断的连接,释放失败进程对数据库对象的锁定,将失败进程的ID从活动进程表中移去。PMON还周期地检查调度进程(DISPATCHER)和服务器进程的状态,如果已死,则重新启动。

同SMON进程一样,PMON进程会被定期地唤醒,以检查是否需要它执行任务;其他进程也可以调用PMON进程。

2.4.3 DBWR数据库写入进程

DBWR进程负责管理数据库缓冲区及数据字典缓冲区。DBWR以批的形式将数据库缓冲区的内容写到磁盘上。当数据库缓冲区的内容被修改时,服务器进程会对已修改的数据块做已修改标记,但不立即启动DBWR进程,而是等被修改的数据块累积到一定数量后才将修改的数据保存到磁盘。

发生下面情况会激发DBWR写磁盘。

❑ 当服务器进程向Dirty List添加一个新的已修改数据块时,发现Dirty List已达到指定的长度,服务器进程就通知DBWR写磁盘。这个长度由初始化参数DB_BLOCK_WRITE_BATCH决定。

❑ 当服务器进程要向数据库缓冲区写数据时,如果搜索LRU清单超过DB_BLOCK_MAX SCAN_CNT次,通知DBWR写磁盘,以腾出空间。

❑ 当超时发生(每3秒钟)时,DBWR写磁盘。

❑ 当检查点发生时,LGWR通知DBWR写磁盘。

一个数据库实例中只有一个SMON和一个PMON进程,但有的操作系统可以设置多个DBWR进程。使用多个DBWR进程有助于减少DBWR冲突。DBWR的数量由初始化参数DB_WRITE_PROCESS决定。如果系统支持异步I/O,用户可以创建多个DBWR I/O从进程。DBWR I/O从进程的数量由初始化参数DB_I/O_SLAVES参数决定。

如果配置了多个DBWR进程,DBWR就不再叫作DBWR,而分别称DBW0、DBW1、DBW2……

2.4.4 LGWR日志写入进程

LGWR进程负责将重做日志缓冲区的内容写入联机重做日志文件。正常情况下,LGWR是唯一从重做日志缓冲区中读数据并向联机重做日志文件写数据的进程。LGWR按顺序向联机重做日志文件写数据。

LGWR是向一组联机重做日志文件写数据,如果一个联机重做日志文件损坏,LGWR则向组内的其他联机重做日志文件写,同时将出错信息写到LGWR跟踪文件和系统的警告日志文件中。

重做日志缓冲区是个循环缓冲区,Oracle只有在缓冲区数据写到联机重做日志文件后才允许新的重做数据覆盖旧的缓冲区。

Oracle实行这样的机制,当数据库缓冲区的修改数据要写到磁盘上,Oracle要先通知LGWR将重做信息写入磁盘。也就是说,当数据库要改变时,LGWR总是要先记录改变内容,然后才允许改变发生。这样如果发生意外,用户可以从重做日志文件中恢复。所以重做日志缓冲区中总是包含着数据库的最新状态。

Oracle使用一种“快速提交机制”,当用户发出提交命令时,先将提交记录放入重做日志缓冲区,与其相应的数据要等到写数据文件时才放到重做日志缓冲区。

当提交一个事务时,Oracle为这个事务分配一个系统改变号(System Change Number)SCN。SCN与事务的重做数据一起放在重做日志文件中。在并行服务器结构和分布式数据库系统中,SCN可以实现数据库同步恢复。

下列情况会激发LGWR写磁盘。

❑ 当用户发出commit命令时,LGWR写磁盘。

❑ 当重做日志缓冲区的1/3满时,LGWR写磁盘。

❑ 当DBWR或DBWn要写磁盘时,LGWR写磁盘。

❑ 当超时发生(每3秒钟)时,LGWR写磁盘。

2.4.5 CKPT检查点进程

检查点进程的作用是控制数据文件、控制文件和重做日志文件之间的协调同步,检查点进程一般和数据库写进程DBWR合作。检查点是在数据库中做一个记号,表示记号之前的数据库是完全正确的,如果数据发生意外,可以恢复到之前这个完全正确的检查点,如图2-10所示。

图2-10 检查点的作用

检查点进程在检查点出现时,对全部数据文件标题进行修改,标识该检查点。在通常的情况下,该任务由LGWR执行。如果检查点明显地降低系统性能,可使CKPT进程运行,将原来由LGWR进程执行的检查点的工作分离出来,由CKPT进程实现。对于许多应用情况,CKPT进程不是必须有的。只有当数据库有许多数据文件、LGWR在检查点时明显地降低性能时才运行CKPT。CKPT进程不将块写入磁盘,该工作是由DBWR完成的。检查点进程由初始化参数CHECKPOINT_PROCESS设定,值为True表示使用检查点进程,而值为False表示不使用,缺省时为False。下列情况会产生检查点。

❑ 上一个检查点之后,超过了初始化参数LOG_CHECKPOINT_TIMEOUT设定的时间,产生检查点。

❑ 上一个检查点之后,当写入磁盘的重做日志块数量超过了初始化参数LOG_CHECKPOINT_INTERVAL的值时,产生检查点。

❑ 日志文件切换时,产生检查点。

❑ 实例关闭时,产生检查点。

❑ DBA发出强制命令要求产生检查点时,产生检查点。

❑ 表空间脱机时(offline),产生检查点。

2.4.6 RECO恢复进程

恢复进程用于恢复分布式数据库环境中失败的事务。数据库恢复进程会自动连接失败的数据库实例,连接成功后,将删除失败的事务和对应的数据行;如果连接失败,恢复进程会在一定时间后再次尝试连接。

要使用恢复进程,数据库实例必须允许分布式事务处理,同时初始化参数DISTRIBUTED_TRANSACTIONS大于0。

2.4.7 ARCH归档进程

LGWR进程以循环方式向联机重做日志文件写入日志数据,当写满第一个日志文件后,就开始向第二个日志文件写入;第二个日志文件被写满后,再向第三个日志文件写入。当最后一个重做日志文件被写满,LGWR就又重写第一个重做日志文件,并将以前的数据覆盖。

ARCH进程作用,就是将填满的重做日志文件转储到指定的设备上。这样先前的重做日志文件不至于被覆盖。

数据库必须在归档(Archivelog)模式下才能使用归档进程。在初始化参数LOG_ARCHIVE_MAX_PROCESSES内用户可以设置数据库最大的可使用归档进程。

2.4.8 SNPn作业队列进程

SNPn进程用来完成一些应用程序的周期性执行工作。SNPn能周期性地唤醒作业队列中的作业,并完成这些作业,自动刷新分布式数据库中表的快照。

在分布式事务处理时,一个数据库实例可以启动36个快照刷新和作业队列进程,这些后台进程的名字以字母SNP开头,以数字或字母结束。当这些进程被定期唤醒,则自动刷新分布式系统中表的快照和执行DBMS_JOB包的数据库作业。

初始化参数JOB_QUEUE_PROCESSES用于设定每一个实例中的刷新和作业进程数。

2.4.9 LCKn锁进程

LCKn锁进程用于锁定数据库对象不被数据库其他进程更改。LCKn锁进程用于并行数据库环境中,在并行数据环境中经常会出现多个进程同时读取同一数据库对象,例如同时读取同一张数据库表。考虑到安全性和数据的一致性,当一个进程在更改这个表时,使用LCKn锁进程锁定这个表,其他进程就不能对这张表进行更改。锁进程最多有10 个,分别命名为LCK0~LCK9。LCKn锁进程的个数由参数GC_LCK_PROCS决定。

2.4.10 Dnnn调度进程

调度进程是共享服务器的一部分,调度进程可以减少多重连接所需要的资源。一个数据库服务器可以建立多个调度进程,支持Oracle服务器的每一个协议都至少创建一个调度进程。调度进程根据服务器的网络配置,在数据库启动时创建,也在数据库启动后再创建或取消。

2.4.11 Snnn共享服务器进程

Snnn共享服务器进程存在于共享服务器中,给共享服务器提供类似于专用服务器的数据库连接,只是共享服务器不与特定用户进程关联。Snnn共享服务器进程的数量在初始化参数SHARED_SERVERS和MAX_SHARED_SERVERS参数之间。

2.4.12 QMNn队列监视进程

QMNn队列监视进程是Oracle高级队列选项所用的后台进程,用于监视实例中的消息队列。初始化参数AQ_TM_PROCESSES值指定了队列监视进程的个数,最大可设为10。QMNn进程和SNPn进程的失败不会引起实例的失败,Oracle会自动重启这些进程;而其他进程的失败则会引起整个实例的失败。

2.5 Oracle物理结构

物理结构就是我们熟悉的一些物理文件,这些文件是数据库的载体,数据库中的所有数据,包括系统数据都存放在这些物理文件中,如图2-11所示。

图2-11 主要的物理文件

2.5.1 数据文件

每一个Oracle数据库都有一个或多个物理的数据文件(Data File)。一个数据库的数据文件包含全部数据库数据。逻辑数据库结构(如表、索引)的数据也需要存储在数据库的数据文件中。数据文件有下列特征。

❑ 一个数据文件只与一个数据库联系。

❑ 一旦建立,数据文件不能改变大小。

数据文件中的数据在需要时可以读取并存储在Oracle内存中。例如,用户要存取数据库一表的某些数据,如果请求信息不在数据库的内存存储区内,则从相应的数据文件中读取并存储在内存。当修改和插入新数据时,系统不必立刻写入数据文件。为了减少磁盘输出的总数,提高性能,数据存储在内存,然后由Oracle后台进程DBWR决定如何将其写入到相应的数据文件。

2.5.2 控制文件

控制文件(Control File)控制数据库的物理结构。控制文件记录了数据库中所有文件的控制信息,维护数据库内部的一致性,并引导数据库恢复。控制文件是数据库的控制中心,对数据库至关重要,所以在一个Oracle数据中同时联机存储了多个复本。这些文件一般存储在各个不同的磁盘上,这样可以降低因磁盘损坏而引起数据库彻底崩溃的风险。创建数据库时,系统同时会创建与之对应的控制文件。控制文件中主要包含以下信息。

❑ 数据库名称

❑ 数据库数据文件和日志文件的名称和位置

❑ 数据库建立日期

❑ 表空间信息

❑ 归档日志信息

❑ 当前的日志序列号

❑ 检查点信息

数据库控制文件的名称通过初始化参数CONTROL_FILES确定。如果需要给数据库添加一个新的控制文件,用户需关闭实例,把已存在的一个控制文件复制到新的地址,把新的地址添加到CONTROL_FILES参数设置值上,然后重新启动这个实例,使新的控制文件生效。

2.5.3 重做日志文件

Oracle用重做日志文件来保存所有数据库事务的日志。当数据库被破坏时,用重做日志文件恢复数据库。

重做日志文件一般是以组形式存在的,每一个数据库有两个或多个重做日志文件(Redo Log File)组,每一组内都有多个镜像文件。如图2-12所示,一共有3组重做日志文件,每组内都有3个镜像文件。

图2-12重做日志组工作方式

Oracle以循环方式向重做日志文件写入:第一个日志文件被写满后,就向第二个日志文件写入,然后依次类推。当所有联机重做日志文件都被写满时,就再回到第一个日志文件,用新事务的数据重写第一个日志文件。如果数据库正以ARCHIVELOG模式运行,在重写联机重做日志文件前,数据库会先将其内容存入归档重做日志文件中,任何时候都可以用这些归档重做日志文件来恢复数据库。

为防止重做日志文件遭破坏,注意,尽量将重做日志文件镜像到不同磁盘上,当LGWR向日志文件写信息时,会同时将信息也写到镜像文件上。

2.5.4 参数文件

参数文件一般以Ora为扩展名,Ora为Oracle的前三个字母。Oracle 9i版本数据库之后系统有以下几类参数文件。

1.初始化参数文件

在启动一个实例时,Oracle必须读入一个初始化参数文件(Initialization Parameter File)。该参数文件中含有实例配置参数,这些参数值决定着数据库和实例的特性,如共享池、高速缓冲、重做日志缓冲分配、后台进程的自动启动、控制文件的读取、自动联机回滚段等。Oracle9i之前,该参数文件名为Init<SID>.ora,该参数文件中主要包含以下几点。

❑ 一个实例所启动的数据库名字

❑ 在SGA中存储结构配置

❑ 数据库控制文件的名字和位置

2.配置参数文件

配置参数文件,一般被命名为config.ora。配置参数文件一般由初始化参数文件调用,在初始化参数文件由ifile参数来指定配置参数文件的位置。

系统中如果一个数据库对应一个实例,则没有配置参数文件;配置参数文件只有在一个数据库对应多个实例时才有。

3.二进制参数文件Spfile

Oracle 9i之前,参数文件是文本文件,Oracle 9i之后采用了和以前版本不同的二进制的参数文件Spfile,同时保留了对原有文本参数文件的支持,所以Oracle 9i之后有两种参数文件:静态文本的Pfile参数文件和Spfile参数文件。后面的章节将会详细介绍这几种参数文件的区别和使用方法。参数文件Spfile不能直接修改,如果直接修改会导致数据库启动失败。

2.5.5 跟踪文件

跟踪文件是由后台进程和用户进程建立的,用于记载进程运行时出现故障的信息。在实例中运行的每一个后台进程都有一个跟踪文件与之相连。跟踪文件记载后台进程遇到的重大事件的信息。

后台进程的追踪文件被命名为<SID>PROC.trc,PROC是后台进程的名字。用户进程追踪文件命名为<SID>_ORA_XXX.trc,XXX是一连串用来唯一标识的数字。

后台进程追踪文件存在BACKGROUND_DUMP_DEST所指定的位置,用户进程追踪文件存储在由USER_DUMP_ DEST参数设置所指定的目录里。

用户可以在V$PARAMETER视图中获得BACKGROUND_DUMP_DEST和USER_DIMP_DEST参数的当前设置。

2.5.6 警告日志文件

除了跟踪文件外,Oracle还有一个称作警告日志(alert log)的文件,警告日志文件记录数据库运行中发生的主要事件的命令及结果。例如,表空间的创建、重做日志的转换、操作系统的恢复、数据库的建立等信息都记录在警告日志中。警告日志是数据库每日管理的重要资源,应经常监控警告日志。因为警告日志记录了数据库操作期间遇到的任何问题,其中包括出现的任何内部错误。当调查数据库故障时,用户应首先查看该文件,关键的错误总是记录在这里。

为使警告日志便于使用,用户最好是每天对其重新命名。例如,如果警告日志称作alert_orcl.log,用户可以重新命名将文件名包括当前日期。下次Oracle要写该警告日志时,找不到具有alert_orcl.log文件名的文件,数据库又会新创建一个。这样,除了有以前的警告日志,用户还有一个当前的警告日志(alert_<SID>.log)。这样有利于今后对数据库历史运行的追踪。

警告日志文件存在由初始化参数BACKGROUND_DUMP_DEST指定的位置。

2.6 Oracle逻辑结构

Oracle逻辑结构是从用户角度来看数据库,对使用Oracle的用户来说,一般只关心数据库的逻辑结构,如关心表、视图等。逻辑结构从操作系统中是看不到的,只能通过数据字典查询到。

2.6.1 逻辑结构概述

从逻辑结构上讲,Oracle包括表空间(Tablespaces)、段(Segments)、区(Extents)、数据块(Data Blocks)以及模式(Schema)。数据库由若干个表空间组成,表空间又由若干个段组成,段由区组成,区是磁盘空间分配的最小单位。区又由数据块组成,数据块既是逻辑单位,又是物理单位,数据库中I/O的读取是以数据块为单位的。

数据库的逻辑结构是要通过物理结构来实现的,数据库的逻辑结构和物理结构的关系如图2-13所示。

图2-13 数据库的逻辑结构和物理结构

2.6.2 表空间

表空间(Tablespace)是数据库的逻辑划分,一个Oracle数据库至少有一个表空间,称作System表空间。为了方便管理和提高系统的性能,系统通常还会划分出一些附加表空间给用户和应用程序,如USER表空间,给一般用户使用,UNDO表空间供回滚或撤销使用。

一个表空间对应于磁盘上的一个或多个数据文件,如图2-14所示。

图2-14 表空间与数据文件

表空间还有如下特点。

❑ 一个表空间只能属于一个数据库。

❑ 一个表空间在某一时刻只能属于一个数据库。

❑ 表空间可以由一个或多个段组成。

❑ 当创建一个新的数据库对象,例如表或索引,Oracle会将对象存储在指定的表空间内;如果没有指定,Oracle会储存在缺省的表空间内。

如图2-15 所示,表空间内的数据库对象存储在数据文件中,数据库对象和表空间的关系如下。

图2-15 表、表空间和数据文件的关系

❑ 当表空间中只有一个数据文件时,Oracle在这个表空间的唯一文件中存储全部的数据库对象。

❑ 当表空间含有多个数据文件时,Oracle可以将数据库对象存储在这个表空间内任意的数据文件中,甚至一个数据库对象可以存储在多个数据文件中。

2.6.3 System表空间、SYSAUX表空间以及其他表空间

2.6.2节讲解了表空间的基本概念,为了让用户能够更深入了解表空间,本节将详细介绍一些常见的表空间,分别为System表空间和SYSAUX表空间等。

1.System表空间

前面讲到,一个Oracle数据库至少有一个表空间,即System表空间。当创建数据时,必须指明System表空间的数据文件的特性,如数据文件名称、大小。System表空间的作用有两个。

❑ Oracle在系统表空间中存储数据库的数据字典(数据字典将在后面讨论,数据字典内存储了Oracle自身的内部结构信息)。

❑ Oracle在系统表空间中存储全部的PL/SQL程序的原代码和编译后的代码,例如存储过程、函数、包、数据库触发器。如果要大量使用PL/SQL,用户就应该设置足够大的System表空间。

2.SYSAUX表空间

SYSAUX表空间是System表空间的辅助表空间,许多数据库的工具和可选组件将其对象存储在SYSAUX表空间内。它是许多数据库工具和可选组件的默认表空间,如Oracle Enterprise Manager Grid Control工具,在Oracle 10g之前,它的对象存在自己的oem_repository表空间内,Oracle 10g则存在SYSAUX表空间内。

3.其他表空间

一个Oracle数据库系统有多个表空间,创建数据库时,除默认创建System表空间外,Oracle还会创建其他表空间,主要有以下几个。

❑ Users表空间,用于储存用户的数据。

❑ Undo表空间,用于事务的回滚、撤销。

❑ Temp临时表空间,用于存放Oracle运行中需临时存放的数据,如排序的中间结果。

Oracle将数据库划分多个表空间,可以独立地管理各个表空间数据,调节各个表空间的属性以适应不同的需要。

根据表空间的不同属性,表空间分为以下几类。

1.联机表空间和脱机表空间

如果不想使用某表空间,用户可以将这个表空间设置为脱机状态。联机表空间表示表空间可用,用户可以访问其中的信息。脱机表空间表示表空间不可用。将表空间设置脱机状态后,表空间仍在数据库内,但用户不能访问其中的信息。

数据库的System表空间必须是联机状态,因为对数据的正常操作都要访问System表空间的数据字典,否则会提示出错。

2.永久表空间和临时表空间

大多数表空间是永久表空间,永久表空间内存储了需永久保存的数据,数据库实例关闭后,数据仍存在数据库中,如一些表、索引、视图等的数据。

临时表空间的作用是给数据库建立一个临时的工作区域,在这个临时区域内处理需要消耗大量内存的操作,如连接查询、大数据量的排序等。

3.表空间和可读写表空间

表空间创建时默认是可读写表空间,用户可以在这个表空间中存储数据,删除或修改数据。但有些情况下,表空间存储的数据不允许改变,例如表空间内存了历史数据,表空间就不宜更改,这时用户可以将这个表空间设为只读。

2.6.4 段

段是表空间的下一个逻辑单位,段位于表空间当中,由若干个区组成,随着数据的增加,可自动增长。段用来存储数据,每建立起一个表、索引或快照,就要分配一个段。一个段对应一个表空间,不可跨越表空间,但段可以跨越一个表空间中的数据文件。

根据存储的数据类型不同,段可以分为数据段、索引段、临时段和回滚段。段的增大是通过分区个数的增加来实现的,下面我们分别详细介绍各段类型的作用。

1.数据段

数据段是数据库中用于存储数据的段,对于没有用到聚集的基表,一个表对应一个数据段。

2.索引段

索引段用于存储索引数据,一个索引对应一个索引段,如果一张表有两个索引,则要建立两个索引段。有关索引的具体内容,可参考后面的章节。

3.临时段

SQL语句执行过程中常要用一些临时的工作区域,例如为大表排序,排序开始时在用户的临时表空间中自动创建一个临时段,排序结束时又自动清除。

4.回滚段

回滚段是Oracle中比较难管理的一个段,回滚段用于存储数据库修改之前的数据,数据修改失败后,可以从回滚段内取出原来的数据恢复。一个事务只能使用一个回滚段,而一个回滚段中有多个事务的回滚。

Oracle将数据写入回滚段的操作不同于其他段的操作。回滚段是一个区间(Extends)的循环,当一个区间写满时,就写下一个区间,如图2-16所示。如果事务太长,以至于要覆盖自己,Oracle就会分配一个额外的区间,以安全存储这个长事务。

图2-16 回滚段的循环使用

System回滚段是在数据库建立时,建立一个称为System系统回滚段的回滚段,System回滚段位于System表空间中。如果使用System回滚段处理数据库事务压力过大,用户可以建立多个回滚段。处理事务时,用户还可以指定用哪一个回滚段做回滚。

回滚段还分为公有回滚段和私有回滚段。一般的回滚段都是公有的,所有的事务都可用公有回滚段做回滚。但当在并行服务器环境下,用户希望在同一个数据库上,不同的服务器用不同的回滚段,这就产生私有回滚段。

Oracle 10g不推荐使用回滚段。Oracle 10g的回滚功能通过Undo表空间实现,这个表空间由Oracle自动管理,其分配与回收不需要数据库管理员干预。

2.6.5 区

区(Extents)又叫范围,Oracle以区为单位为数据分配储存空间,所以区是Oracle分配磁盘空间的最小单位,一个区由若干个数据块组成。

在数据段中,区的个数受到区的参数限制。在初始创建段时,系统只分配最小的区个数MINEXTENTS。随着段的数据量加大,区也随之增多,但不能超过参数MAXEXTENTS的限制。如果超过了最大区个数,用户就不能再向段中添加数据,否则数据库出错,如图2-17所示。

图2-17 数据段的增长过程

2.6.6 数据块

数据块是数据库中最小的数据管理单位,Oracle使用数据块存储和提取磁盘上的数据。数据块是在数据库创建时指定的,数据库创建后不能修改。数据块的大小必须等于服务器数据块的大小或它的整数倍。例如,服务器的操作系统块大小为512K,数据块的大小只能是512K、1024K、2048K等。

Oracle 8i以前,数据库使用统一的大小,Oracle 9i和10g内数据库可以同时使用2K、4K、8K、16K、32K大小的数据块。对于系统表空间和默认表空间使用初始化参数DB_BLOCK_SIZE决定大小,其他表空间则可以用初始化参数DB_nK_CACHE_SIZE的大小。

1.数据块的格式

每一数据块的格式是类似的,不管它是包含表、索引还是聚集数据,其格式如图2-18所示。

❑ 标题:包含一般的块信息,如块地址、段类型(数据段、索引段、回滚段等)。

❑ 表目录:包含存放的表的信息。

❑ 行目录:包含块中的行的信息,如数据区中的每一行的首地址。

❑ 行数据:包含表或索引数据。

❑ 未用空间:可用于插入新行或用于修改行的空闲空间。

2.数据块的可用性和空闲列表

当创建新的数据库对象时,如创建表、索引,Oracle会为对象的相应段分配一个或多个区。因为其中经常会修改或删除,所以系统会留下许多空闲块。这些空闲块被一个空闲列表串接起来,如图2-19所示。

图2-18 数据块的格式

图2-19 数据块的空闲列表管理

由图2-19可以看到数据块如何删除和加入空闲列表。当向表空间中插入一条新记录时,空闲列表上的所有数据块都可以用来储存新记录,当事务向表中插入越来越多的记录时,数据块空闲比例少于一定阀值(即数据库对象的PCTFREE参数值),Oracle就从空闲列表中将此数据块删除。删除数据时,如果数据块中数据占用的比例大于一定阀值(即数据库对象的PCTUSED参数值),Oracle会重新将这数据块加入到空闲列表中。

3.行链接与数据块的大小

向表中插入新的记录时,Oracle将新纪录放到表的空闲列表的数据块上。最好的情况是该记录能够放入到一个数据块中。这样查询表时,Oracle只需从磁盘读取一个数据块就可以取出全部的这行记录数据。如果某记录存储在两个或多个数据块上,即产生了行链接,读取这行记录时就要读取多个数据块,这就增加了磁盘I/O的压力,所以应尽量避免行链接。图2-20是一个表示行存储的示意图。

图2-20 行存储示意图

当更新一个记录时,Oracle也可能会产生行链接,因为记录更新后,原数据块存储不下更新数据,所以不得不跨数据块存储。

避免行链接的方法之一是合理配置好数据块的大小。如果数据库中有很多超过缺省尺寸的记录,设置较大的数据块会有更好的效果。合理配置PCTFREE、PCTUSED参数的值也能减少行链接。

2.6.7 用户与模式

用户不是数据库中的一个物理结构,但与数据库中的对象有重要的关系,用户拥有数据库对象。例如,Oracle的系统用户SYS拥有Oracle系统字典表,这些字典表存储了数据库的结构信息;用户SYSTEM拥有可访问数据字典的视图,其他用户可访问这些视图。

数据库创建对象的时候必须在用户下进行,可以定义一些用户参数,使指定的表空间作为用户的缺省表空间。

模式是用户拥有的对象的集合。模式是关系型数据库中一个重要的概念,Oracle在模式中组织管理关系型数据库对象。例如,在模式中组织全部的表、视图、索引等。模式是个逻辑概念,和数据库的物理存储没有任何关系。

Oracle中,用户与模式的概念紧密相连,常常被人误以为是一个概念。用户与模式是一一对应的,而且名字一样,但是是不同的概念。用户拥有数据库对象,而模式是数据库对象的集合,可以说某人以某用户登录数据库,而不能说某人以某模式登录库。在其他的关系型数据库中,用户和模式的差别很明显。

2.6.8 表

Oracle系统中将关系模型中的关系称为表。表是Oracle数据库中数据存储的基本单位,其数据按行、列存储。每个表由表名以及列集合组成。每个表最多包含254 列。每一列有列名、数据类型、宽度和精度。对于表的每一列可指定完整性约束。

表2-2 常见的数据类型

一般,表的持有者(Owner)就是该表的建立者,但DBA用户可为别的用户建立表。这时,DBA为表建立的者,但不是表的持有者。

Oracle中的表的存放和物理文件并不一一对应,这和其他数据库不同。对没用使用聚集(Cluster,后面会介绍)的表,Oracle会为表在指定的表空间中自动分配一个数据段,在这个段中存放表的数据,表数据以行的方式存放。表的行存储有以下几个特点。

❑ 从逻辑上讲,每个表的行数没有限制。

❑ 数据存储在物理的数据块上,行可以分散在个数据块上,块之间用块链接连起来。

❑ 各行之间的存储在物理上没有空隙。

❑ 列长为0表示NULL。

❑ 空值不存储。

当表有很高的并发性要求时,用户可以将这张表存储在多个区(Extents)中,这些分区可以位于不同的表空间中。Oracle支持按某个字段值的范围进行分区。一个分区就是一个段,用户可以为每个分区配置不同的存储参数。

2.6.9 列与数据类型

创建表时,用户要确定表中各列的属性,从而确定表基本结构。每一个列有一个数据类型,数据类型限制了列可以接受哪些数据。

Oracle支持很多数据类型,不管是数据库管理人员,还是开发人员,都要对Oracle的数据十分了解。表2-2列出一些常见的数据类型。

除了表2-2 所列的数据类型以外,Oracle还支持ANSI/ISO(ANSI,American National Standards Institute;ISO,International Organization of Standards)的标准数据类型。如果用ANSI/ISO数据类型建表,Oracle会自动转化为表2-3所示的数据类型。对应的转换如表2-3所示。

表2-3 ANSl/lSO标准数据类型和Oracle数据类型的对应

2.6.10 ROWlD伪列

ROWID标识Oracle中物理上独一无二的一行,它表示的是行的物理地址。Oracle通过ROWID建立表的索引和表之间的连接。

ROWID是Oracle的伪列,在表中实际并不存在该列,更没有该列的数据。但在对表进行操作时,用户可以像一个实际存在的列一样对它读取,如下SQL语句所示。

        SQL>SELECT ROWID, ENAME FROM EMP;%ROWID在EMP表中并没定义

ROWID分物理ROWID和逻辑ROWID。物理ROWID内存放的是普通表、聚集表、分区表等的行物理地址。使用物理ROWID能最快地找到行,因为物理ROWID存放的是物理地址。一般行建立后,这个行的ROWID就固定了,除非行的存储位置移动,例如用Export和Import工具重装了数据库。物理的ROWID又分为两种:在Oracle 7和Oracle 7之前叫Restricted ROWID,Oracle 7之后叫Extended Rowids。

1.Restricted ROWlD

Restricted ROWID用二进制表示物理地址,用SQL语句查询这个ROWID时,会自动将它转换为16进制的VARCHAR2字串。Restricted ROWID包括三部分,数据块号,行号和数据文件号。

❑ 数据块号:行所处的数据块号。

❑ 行号:在块中的行号。

❑ 数据文件号:所处的数据文件号。在同一个数据库中,数据文件号是唯一的。每一个数据库的第一个数据文件号都是1。

举例说明,ROWID为:00000DD5.0001.0002。DD5就是数据块号;0001就是在块中的行号;0002就是数据文件号。

2.Extended Rowids格式

新的Extended Rowids使用字符A-Z,a-z,0-9,-,+表示行。Extended Rowids使用的是相对值,所以必须存放Segment的标识,否则相互之间会混淆。Oracle 8在ROWID中加入数据库对象的Segment号,用来标识对象是TABLE还PARTITION。

Extended Rowids的格式分四部分:OOOOOOFFFBBBBBBRRR。

❑ OOOOOO:代表数据库对象所处的段号。

❑ FFF:代表表空间中的数据文件号。

❑ BBBBBB:行所在的数据块号。

❑ RRR:在数据块中的行号。

举例,如ROWID为:AAAAaoAATAAABrXAAN,AAAAao就是对象的段号,AAT就是数据文件号,AAABrX是行所在的数据块号,AAN是行在数据块中的行号。

3.逻辑ROWlD

对索引组织表(参考2.6.12节),并没有固定的物理地址,它的行存储随着表的插入经常移动,所以不能用物理ROWID来读取该行。为了标识该行,Oracle提供了一个逻辑ROWID的方法,逻辑ROWID是通过表主键建立。Oracle通过逻辑ROWID建立二次索引。

2.6.11 约束条件

约束条件(Constraint)就是强制数据库的表列满足一个特定的规则,当向这个表中插入或修改时,必须服从这些约束条件,否则不进行这些操作。Oracle数据库中.约束条件包括Primary Key、Not Null、Check、Unique和Foreign Key五种类型。

❑ Primary Key约束,即主键约束,可以是表中的一个或多个列,它用于标识每个行的唯一性。数据库中的主键列必须非空(Not Null),并且值不能重复。

❑ Not Null约束,即非空约束。如果数据库的某列规定Not Null,那么在向该列插入数据时,该列必须有数据。

❑ Check约束即检查约束,确保列的值要符合一定条件,如月份必须大于1小于12。

❑ Unique约束,即唯一性约束,表示该列数据值不能相同。

❑ Foreign Key,即外键,表示该列值是引用其他表的列。定义外键的列叫从表,而被引用列的表叫主表。

2.6.12 索引

索引和表一样,是数据库中十分常见的概念,索引为提高数据检索性能而建立,利用它可快速地确定所要检索信息的物理存储路径。

在数据库中,性能的一个主要衡量标准是就是数据库的存取速度,而磁盘I/O又是决定存取速度的重要因素,访问I/O越少性能越好。使用索引能减少使用I/O的次数。索引类似于书的目录,在书目录中找内容,会比到书正文中一页一页找内容要快得多。

索引在逻辑上和物理上都独立于表数据,它在表外存了被索引列的全部数据和数据在表中的行地址。它的建立或删除对表没有影响,所有应用可继续处理。索引数据的检索性能几乎保持常数。索引和表的关系如图2-21所示。

图2-21 表和索引的关系

索引可在表的一列或多列上建立,一旦建立后,Oracle自动维护和使用,索引对用户是完全透明的。索引有唯一索引和非唯一索引,一个索引的索引列最多为16列。Oracle提供几种不同类型的索引实现方式,B树索引、位图索引、反向键索引以及函数索引。

1.B树索引

B树索引是最常用的索引,它的存储结构类似书的索引结构,有分支节点和叶节点,分支节点相当于书的大目录,叶节点相当于具体到书页的索引。

B树索引是Oracle缺省使用的索引。分支节点包括两部分:用于分支判断的一个值和指向下一层节点的指针(即下一个节点的物理位置)。叶节点也包含两个部分:该记录的索引列值和该记录的ROWID(即行的物理存储位置)。叶节点的块是双向链接的,这样可以以索引值快速地进行升序和降序扫描。一棵典型的B树如图2-22所示。

图2-22 B树索引结构

图2-22假设索引列是整数,如要查找索引值为11的行,先从根节点10,再找到15,最后取到值为11数据块,从中取出行地址,中间不需要全表扫描。为了简化,图中的分支节点只存了一个关键字。实际情况中,用户可根据数据块的大小存多个关键字,假如存了n个关键字数,一个节点需存n+1个指针。

2.位图索引

位图索引也是按B树索引组织的,但它在叶子中存的是位图,而不是ROWID列表。位图索引用位来表示索引健值,当行中包含这个索引键值,就将该位置1,然后用一个映射函数将该位转化为ROWID。位图索引(Bitmap Indexes)只有Oracle企业版中才提供。

下面我们举个例子,如图2-23所示,给Parts表加了两个索引:颜色和尺寸,颜色有三种:红、绿、蓝。尺寸有三种:大、中、小。

图2-23 位图索引的使用

进行查询时,如上图2-23所示的Select语句,根据where语句内容生成一个二进制列,将对应条件值中“红“和“中”的位置1,其他位置0,分别和索引位图相“与”,结果为“真”的就是所要的结果,如图2-23所示,取出了“002”,“005”行。位图索引的优点有几个。

❑ 用一个位来表示一个索引健值,节省了存储空间。

❑ 对and,or或=的查询条件,位图索引有很高的效率,计算机善于处理0,1的数据。

❑ 位图索引适合决策支持系统与数据仓库的数据查询。

❑ 以下几种情况不适合使用位图索引。

❑ 经常插入或更新的表,不适合使用位图索引。

❑ 索引列的值很多,值可选范围大,这种情况下建立索引的代价很高。

3.逆键索引

逆键索引(reverse key indexes)就是将除ROWID列之外的每一个索引列的字节颠倒,但保持列的顺序。逆键索引的目的就是解决实时应用簇(Real Application Clusters,参考后面章节)对索引的读写总是集中在一小块地方的问题(对同一数据块的频繁读写,容易引起磁盘坏道,而且易导致I/O瓶颈)。将索引列值颠倒后,系统能将对索引的读写分散到索引的整个区域。使用逆键索引能提高OLTP实时应用簇的性能,但缺点是不能用逆健索引进行范围检索。

4.函数索引

函数索引(Function-Based Indexes)和B树索引类似,只不过在索引内存储的是一个函数的计算结果。举例说明,创建一个函数索引,这个函数使用Oracle内部函数UPPER,将name都改成大写存储。

        SQL>CREATE INDEX uppercase_idx ON employees(UPPER(name));
        SQL>SELECT * FROM employees WHERE UPPER(fname)= 'RICHARD';

因为索引内存储的是name的大写,所以查询的速度会比没用函数索引快。使用函数索引前用户要将初始化参数QUERY_REWRITE_ENABLED设为TRUE,将QUERY_REWRITE_INTEGRITY设为TRUSTED,同时将COMPATIBLE参数设成8.0.1以上。

5.索引组织表

索引组织表(Organization Index)将表和数据存储在一起,索引组织表缩写为IOT。索引组织表适合主要通过主键访问数据的表。

一张索引组织表就是一棵B树,只是叶子节点上存储的不是ROWID,而是整行的数据,如图2-24所示。因为表行和索引存在一起,所以每一行没有物理的ROWID,索引组织表使用逻辑的ROWID,对索引组织表的访问和其他表的访问方法一样。

图2-24 表和索引组织表

用户还可以在索引组织表上建立二次索引。建立二次索引时,Oracle使用逻辑ROWID。普通表和索引组织表的区别如表2-4所示。

表2-4 普通表和索引组织表的区别

2.6.13 视图

在关系模型中我们将视图称为虚表,它是由一个或多个表(或视图)中的数据提取组成,用查询来定义,所以视图也叫存储的查询(Stored Query)。视图在许多地方可以当作一般表使用。

但视图与表不同,一个视图不分配任何存储空间,视图不真正地包含数据,而由查询获得数据。因为视图是由表导出的,所以视图与表存在着许多类似。和表一样,视图最多可定义254列。视图只能用来查询,而不能对它进行修改、插入或删除。视图有以下四个优点。

❑ 视图可以限制对表的存取,因此视图为表提供附加的只读安全性。

❑ 视图可以隐藏数据的复杂性,例如一个视图可用连接操作定义,从多个表中抽取所需的行和列。视图可隐藏信息来源。

❑ 视图可以简化操作命令。

❑ 视图可以实现表的存储场地透明性。用户只需关心视图的结构,而不必关心数据如何存储。

但使用视图会降低数据库的存取速度,因为用视图提取数据时,数据库要先解析视图,再从基表中取数据。

2.6.14 序列

序列提供唯一的一个数值来简化程序设计工作。序列(Sequence)定义存储在数据字典中。当一个序列第一次被查询调用时,它将返回一个预定值。在随后的每一次查询中,序列将产生一个按其指定的增量增长的值。序列可以是循环的,或者是连续增加的,直到指定的最大值为止。

序列不可回退,每当数据库看到取下一个序列值的命令,序列值就加一个指定值。即使事务执行失败,数据库也不会退回原来的值。

2.6.15 过程与过程包

过程(Procedure)是一个PL/SQL语句块,它存储在数据字典中并且可被应用程序调用。用户可以使用过程存储数据库中频繁使用的应用逻辑。当执行一个过程时,这个过程中的语句将作为一个整体执行。过程不返回任何值。

系统用户可以使用存储的过程来帮助实施数据的安全性。系统用户可以撤销用户直接访问应用程序中的一些表的权限,而是授权用户执行访问这些表的一个过程。当执行过程时,它将以过程拥有者的权限来执行对表的访问。除此之外,用户就不能访问这些表。

系统用户可以使用过程包(Package)将过程及函数安排在一个包中,这样便于用户使用和系统管理。包说明和包体都存储在数据字典中。过程包在过程及函数所需要的管理任务中非常有用。

过程包中的不同元素可以定义为“公用”或“私有”。过程包的用户可以使用公用元素,私有元素则对用户是隐藏的,它只能被其他过程调用。

函数、过程包和过程的源代码都存储在数据字典表中。如果应用程序经常使用软件包,就必须增加System表空间的容量以适应数据字典容量的增长。例如Oracle财务软件(Finacial)工具中就可能需要一个大于250MB的System表空间。过程包的数量及复杂性在使用中直接影响着SGA的SQL共享池(Shared SQL Pool)部分的大小。

2.6.16 函数

函数(Function)是存储在数据库中的代码块,和其他语言的函数类似,Oracle函数可以把值返回给调用它的程序。

用户可以建立自己的函数,并在SQL语句中调用它们,就像执行Oracle内部函数一样。

例如,Oracle提供SUBSTR内部函数来截取字串,用户可以自己定义一个新函数,如称作MY_SUBSTR,在MY_SUBSTR函数中用户不但可以保留原来SUBSTR的功能,而且可以加去掉两头空格的功能。这个新函数的使用和Oracle内部函数一样。对使用PL/SQL进行程序开发的程序员来说,写函数是司空见惯的事情。

2.6.17 触发器

触发器(Trigger)也是存储在数据库中的过程,一个特定的数据库事件发生时就执行这个过程。触发器可以用来检查数据库的完整性,实施附加的安全等。

Oracle中允许对表发出的INSERT、UPDATE或DELETE语句隐式地执行定义的过程,这些过程称为数据库触发器。触发器与相关表分别存储。触发器只可以定义在表上。在许多情况下,触发器用于很高级的专用数据库管理系统,来补充ORACLE的标准功能。触发器一般用于以下各种情况。

❑ 自动地生成导出的列值

❑ 防止无效的事务

❑ 实施更复杂的安全性检查

❑ 在分布式数据库中实施跨越结点的引用完整性

❑ 实施复杂的事务规则

❑ 提供透明事件日志

❑ 提供高级的审计

❑ 维护表复制同步

❑ 收集与表存取有关的统计

触发器和约束条件都可以用于约束数据的输入,但它们之间有区别。约束条件强制要求表的所有数据都必须满足这个约束条件。而触发器只约束在触发器定义之后的数据,所以不能保证表中全部数据符合该触发器的规则。触发器实施瞬时约束,即在数据改变时实施约束。

触发器由三部分组成:触发事件或语句、触发器的限制、触发器动作。触发事件或语句指激发触发器的SQL语句,如对指定表INSERT、UPDATE或DELETE语句。触发器的限制实际上为一个布尔表达式,当触发器激发时该条件必须为TRUE。触发器的限制是用WHEN子句来指定的。触发器的动作为一个PL/SQL过程,由SQL语句和PL/SQL语句组成。当触发语句发出,触发器的限制计算得TRUE时,执行它。

触发器的类型分为行触发器与语句触发器。行触发器指对受触发语句影响的每一行,行触发器激发一次。语句触发器指语句执行一次,触发器被激发一次,而与涉及的行数无关。

触发器可以指定触发时间,指定动作的执行是在语句执行之前还是在之后。BEFORE触发器指在语句执行之前执行触发器动作;AFTER触发器指在语句执行之后执行触发器动作。

2.6.18 同义词

同义词(Synonym)是表、视图、快照、序列、过程、函数或包的别名。同义词的定义存储在数据字典中。一共有两种同义词,公用(Public)和专用(Private)。公用同义词可以供数据库中每一个用户存取。专用同义词只供定义者或授权的用户使用。同义词可用于以下两种情况。

❑ 同义词可屏蔽对象的名字及其持有者。

❑ 同义词可为分布式数据库的远程对象提供位置透明性。

2.6.19 权限及角色

权限就是用户能否进行某种操作,为了保护数据库的安全必须为数据库设置多个权限。如某张表,一些用户可以对它修改,而另一些用户只能浏览。

角色是权限的组合。数据库中有许多的权限,如果对每个权限分别管理,是个很大的工作量。如果将这些权限分组,用户需要某种权限直接就将一组权限授予他,比逐个授予要方便得多,这就是角色。Oracle提供了一系列对权限进行操作的SQL命令。角色有以下特点。

❑ 角色不是一种数据库对象,不属于任何数据库用户。

❑ 角色的定义存储在数据库的数据字典中。

❑ 角色中可以包含许多系统特权与对象特权。

❑ 角色授权给用户时,该用户就获得该角色定义的所有权限。

❑ 角色权限可以授予用户,也可以授予另一个角色。

❑ 用户可以打开和关闭权限的使用。

2.6.20 快照

快照(Snapshot)就是把远程数据库的数据复制到本地数据库,它用于分布式数据库和对实时性要求不强的数据库系统中。本地数据库通过数据库链接选择远程数据库上的数据,然后在本地保存一个副本,可以设置数据库定时自动刷新。用户可以查询快照。使用快照有以下优点。

❑ 从本地读取快照表比从远程服务器上读取表要快得多。

❑ 建立了远程数据库的快照后,如果远程数据库不可用,还可以继续使用本地的快照。

快照可以设置成只读方式或可更新方式。如果要加速数据读取,还可以对快照使用索引。

2.6.21 聚集

聚集(Cluster)就是把经常访问的表在物理上存储在一起,是存储表数据的一种可选择的方法。它将具有同一公共列值的行存储在一起,这些公共列构成聚集码(Cluster Key)。聚集码在建立聚集时定义。建立聚集时,系统为它建立一个数据段,在建立聚集后必须为它的聚集码建立索引,之后才能执行SQL语句。聚集存在与否,对用户或应用是透明的,因为对存储在聚集中的表数据的存取方法与非聚集的表数据的存取完全一样。组成聚集码的列不能超过16列,其值的长度不能超过数据块可用空间的三分之一。

聚集是一个不容易理解的概念,为便于理解,我们详细介绍聚集的存储原理。假设有两张表EMP和DEPT,表的内容分别如表2-5、表2-6所示。

表2-5 举例用表EMP表

表2-6 举例用表DEPT表

❑ 在不使用聚集的情况下,这两张表的数据分别存储在各自的段中。假如要取出部门号为D1的员工名称和员工所在部门的名称,系统要先从EMP表中取出D1对应的所有员工名称和其所在的部门号,这里是Joan和D1,然后根据部门号取出部门名称Market,这期间至少要访问I/O两次。

而如果用聚集,如图2-25所示,系统先通过聚集码索引找到部门D1的数据块1,然后将数据块1中的数据全读取出来。这样只需访问一次I/O,大大提高了数据存取的效率。

图2-25 聚集的存储方式

因为聚集将不同表的相关行存储在一起,所以可改进聚集表的存取时间,减少磁盘的I/O次数。并且数据块内只存储了一个聚集码值,有效地节省了存储空间。但聚集可能会降低一些操作的执行速度。如对同一张表,不使用聚集时执行插入、修改、删除操作比使用聚集快得多,因为对聚集的这些操作容易引起聚集表的物理重组。因此聚集适用于更新较少的表。而且如果对聚集进行全表扫描,还可能会引起更多的I/O操作。

2.6.22 散列聚集

2.6.21节的聚集使用索引寻找数据块,而散列聚集(Hash Cluster)和索引聚集不同。散列聚集根据Hash函数计算聚集码产生的结果,在聚集中物理地存储记录。产生相同结果的所有记录将存储在相同的数据块或散列桶(Hash Bucket,桶中的Hash值都一样)中。当要查找表中的记录时,系统用Hash函数计算后根据计算的结果到不同的桶中取记录。服务器可能只用一次磁盘I/O就可以取出全部记录数据。

散列聚集的缺点是对查找一定范围的记录效率低,而且对于频繁地数据插入与更新操作,散列聚集的效率不高。

2.6.23 数据库链

数据库链是一个本地(Local)数据库到远程(Promote)数据库的路径。Oracle通过建立数据库链访问远程数据库。数据库链用两种,一种是公用数据库链,本地数据库中的所有用户都可以使用;一种是私有的,专门为某个用户创建。

创建一个数据库链接时,必须指定与数据库相链接的用户名、用户的口令以及与远程数据库相链接的服务器名字。如果不指定用户名,Oracle将使用本地用户名和口令来建立与远程数据库的链接。下面是创建一个公用链接的例子,这个例子打开由网络服务(service)“DB1”指定的数据库。当链接上这个数据库后,我们用用户“HR”、口令“HR1”登录。服务名在本地网络配置文件tnsnames.ora定义。

        Create public database link my_link   %数据库链接名称my_link
        connect to HR identified by HR1
        using 'DB1'

除了在表名后面加限定范围“@ my_link”后缀外,对远程表的操作与对本地表的操作一样。

2.7 数据分区

巨型表或索引由于太大,在数据库系统会引起类似以下几条的很多问题。

❑ 因为表变得非常大,以至于要花费大量的时间对这张表进行管理。

❑ 对巨型表进行全表扫描,会花费系统大量的时间和资源。

❑ 一个事务要查询这张巨型表,如果由于磁盘错误,读写一个数据块发生错误,整个表就变得不能用。管理员恢复这张表要耗费很长时间。

为减少这类问题的发生,Oracle使用分区表和分区索引。

2.7.1 分区表

Oracle允许将表的存储分离为一些空间较小的存储单元,这些小的存储单元就称为分区。每个表的分区含有相同的数据类型和完整性约束,但每个分区之间可以含有不同的物理属性。例如,Oracle能够在不同的表空间中存储一个表的各个分区,并且每一个分区都可以设置pctfree、pctused等存储参数,分区表与普通表的物理存储如图2-26所示。

图2-26 分区表、索引与非分区表、索引

表分区有以下优点。

❑ 用户能对表分区单独创建、重建索引,能单独装载、备份恢复表分区数据。因为只对指定的表分区进行操作,所以比对整张表进行操作快得多。

❑ 表分区能提高查询性能。一般要查询的数据都在一个表分区中,所以不必对全表扫描,而只需对分区扫描。

❑ 表分区可以提高对表的并发操作。不同事务可以对同一表的不同分区同时进行操作。

❑ 分区表对用户来说是透明的,用户可以像操作一张普通的表一样操作分区表。

2.7.2 分区表的类型

Oracle表有几种分区方法:范围分区(Range Partitioning)、列表分区(List Partitioning)、散列分区(Hash Partitioning)、复合分区(Composite Partitioning)。

1.范围分区

Oracle支持范围分区表,根据表分区键值决定数据存储的分区。表分区键值是一个表字段值或多个字段值的组合(最多16 个字段组合),但这些字段不能是LEVEL、ROWID或MLSLABEL等伪列。为防止表分区之间记录迁移,表分区键值不宜修改。为了理解范围分区表,下面我们举一个创建范围分区表的例子。以下是一张销售清单表,以销售日期为分区键值分区。

        CREATE TABLE sales
            (invoice_no NUMBER,
              sale_year  INT NOT NULL,
              sale_month INT NOT NULL,
              sale_day   INT NOT NULL)
          STORAGE (INITIAL 100K NEXT 50K) LOGGING
          PARTITION BY RANGE (sale_year, sale_month, sale_day)
            (PARTITION sales_q1 VALUES LESS THAN (1999, 04, 01)
              TABLESPACE tsa STORAGE (INITIAL 20K, NEXT 10K),
              PARTITION sales_q2 VALUES LESS THAN (1999, 07, 01)
              TABLESPACE tsb,
              PARTITION sales_q3 VALUES LESS THAN (1999, 10, 01)
              TABLESPACE tsc,
              PARTITION sales q4 VALUES LESS THAN (2000, 01, 01)
              TABLESPACE tsd)
          ENABLE ROW MOVEMENT;

2.列表分区

列表分区和范围分区不同,范围分区以分区键值的范围为标准划分分区,而列表分区按照预先给定的一系列离散值进行分区。新数据插入表中时,根据分区键值找到对应分区。以下是建立列表分区的例子。

        CREATE TABLE sales_list
        (salesman_id  NUMBER(5),
        salesman_name VARCHAR2(30),
        sales_state   VARCHAR2(20),
        sales_amount  NUMBER(10),
        sales_date    DATE)
        PARTITION BY LIST(sales_state)
        (
        PARTITION sales_west VALUES('California', 'Hawaii'),
        PARTITION sales_east VALUES ('New York', 'Virginia', 'Florida'),
        PARTITION sales_central VALUES('Texas', 'Illinois'),
        PARTITION sales_other VALUES(DEFAULT)
        );

如以下一行数据,因为在分区sales_west中有 'Hawaii',所以这一行插入sales_west分区中。

       (10, 'Jones', 'Hawaii', 100, '05-JAN-2000')

因为列表分区只根据离散值指定分区,所以有很多记录并不对应任何值,列表分区就将这些记录存储在一个默认的分区中。需要注意的是,列表分区只能用一列分区键值,而不能像范围分区一样能定多个分区键值。

3.散列分区

散列分区的分区方法完全不同于范围分区和列表分区。散列分区先通过散列函数对分区键值计算,根据计算结果确定记录存储到哪个分区。散列分区的定义比较简单,使用也比较方便。散列分区适用于以下几种情况:

❑ 事先不知道指定的范围内将会有多少数据,此时可以用散列分区。

❑ 如果用范围分区,各区之间数据量差异将会很大,并且很难人工干预达到各分区间数据量平衡,此时可以用散列分区。

以下是一个定义散列分区的例子。

        CREATE TABLE sales_hash
        (salesman_id  NUMBER(5),
        salesman_name VARCHAR2(30),
        sales_amount  NUMBER(10),
        week_no      NUMBER(2))
        PARTITION BY HASH(salesman_id)
        PARTITIONS 4
        STORE IN (ts1, ts2, ts3, ts4);

4.复合分区

复合分区是将范围分区和列表分区或散列分区混合使用的一种分区方法。复合分区在分区上用的是范围分区,而在每个分区上又可以用列表分区法或散列分区分成多个子分区,所以复合分区又分为范围散列分区和范围列表分区两种类型。图2-27是范围列表分区的存储示意图。

图2-27 范围列表分区存储

图2-27中纵向是根据月份来划分的范围分区,每个范围分区又以列表方式划分为3个子分区。使用复合分区的优点是能吸取多个分区类型的优点。

2.7.3 分区索引

与表一样,索引可以分区,叫作分区索引,每个索引必须有相同的索引列,但可以有不同的存储特性。一个索引的分区键值决定在哪个分区存储索引项。一个索引的分区必须包含一个或多个被索引的列。索引分区的定义和表分区定义类似。不论是分区表还是非分区表,用户都可以给它建立分区索引。

2.7.4 索引的类型

只有当索引本身很大时,分区索引才有必要创建。分区索引的主列选择和普通索引的主列选择一样。分区索引有两种类型:本地索引和全局分区索引。

1.本地索引

如果两个或两个以上的数据库对象有相同的逻辑分区属性,例如两张分区表有相同的分区键值和相同的分区范围,那么它们就是相同的分区。如果一张分区表和分区索引具有相同的逻辑分区属性,那么这两个索引就是具有相同分区的本地索引,如图2-28所示。

图2-28 本地索引的使用

创建本地索引比较容易,范围说明和最大索引值可以不指定。分区索引会自动从分区表中获得这些属性。

2.全局索引

用户可以创建一个和分区表不同分区属性的索引,这就是全局分区索引。全局分区索引覆盖了整个表的范围,和表分区没有任何联系,不分区的表也可以创建全局索引。全局索引在OLTP环境下能够提供比本地索引更好的性能。图2-29是全局索引的使用示例。

图2-29 全局索引的使用

2.7.5 表分区和分区管理

用户可以对表的每个分区进行单独命名,也可以像操作一张普通表一样对这个命名的表分区进行存取。使用命名表分区能够方便开发人员,并且能减少查询的冗余。虽然开发人员可以像操作表一样操作命名分区,但是表分区毕竟涉及到表的物理存储方式,还是尽量不要轻易使用。Oracle提供了很多操作分区表的方法,这些方法对表调优很有用。

❑ 分区表和非分区表可以互相转化。

❑ 用户可以在一个已有的表分区后添加新分区。

❑ 用户可以分割合并表分区。

❑ 用户可以删除表中不含任何记录的分区。

❑ 用户可以删除指定的表分区而不删除整个表。

2.8 数据库访问

Oracle是大型的多用户并发数据库系统,本节介绍使用Oracle实现多用户并发的基本概念和相关机制。

2.8.1 竞争性、并发性与一致性概念

竞争性、并发性与一致性是所有多计算机系统都会遇到的问题。如计算机网络中文件服务器阻止多个文件同时修改同一个文件,在Windows平台下经常提示另一个用户正在使用此文件、不能修改等。当两个用户要同时获取同一资源时,例如要同时获取打印机,就会产生竞争性。当多个用户获取同一资源,好像每个用户在独自使用该资源时,产生并发性。

Oracle是一个多用户数据库系统,在它内部必须解决资源进程并发性与一致性,使用户对数据库并发、安全的访问。Oracle的并发性与一致性机制并不会使数据库的访问复杂化或降低系统性能。例如,如果几个用户同时想更新表中的同一记录行,Oracle会使用自动锁机制,使事务串行发生,好像是独立发生一样。如事务要查询另一个事务正在更新的表时,Oracle的多版本机制自动允许查询立即执行,并让查询读取更新前的数据。

一致性内最重要的是读一致性,Oracle采取以下措施保证读一致性。

❑ Oracle保证SQL语句在操作某些数据时,其他事务不能更改这些数据。

❑ 当一个事务读数据库数据时,这些数据也可以让其他事务读。

❑ 当一个事务在写数据时,其他事务仍可正常读这些数据。

❑ 但一个事务A在修改一行记录时,其他要更改同一行记录的事务必须要等到事务A完成更新后才能开始对这一行的更改。

2.8.2 事务之间的冲突

多用户数据库系统中,事务具有几种不同形式相互的影响,包括更新丢失、脏读、非重复读等。

1.更新丢失

系统允许多个事务同时更新同一数据时,会发生更新冲突。例如,两个用户都要更新同一表的同一行数据,一个更新就会覆盖掉另一个更新。为防止这种冲突发生,Oracle采取锁机制,防止同时发生更新操作。

2.脏读

当一个事务读取另一个打开事务还未提交的数据时,就会产生脏读。例如,用户A想修改某表中的一行记录,如果该用户还没提交修改,而另一个用户B想查询这行记录,如果返回的是A用户修改后的数据,就是脏读。

Oracle数据库会尽量避免脏读的产生,脏读容易产生问题。如果读取没有提交的数据,而该数据提交失败,那么读出的数据就是无效的,甚至是错误的。Oracle中有一些机制能保证不出现脏读。

3.可重复读与非重复读

在同一事务中,查询返回的是同一数据集,忽略其他已提交或未提交的事务所做的修改,这时发生可重复读。只有当前事务结束、又开始一个新的事务时,查询才能看到最新的结果,这就是可重复读。而如果查询在相同的事务中多次进行,每次都返回最新的数据集,这时发生非重复读。

2.9 锁

为阻止并发事务对数据的一致性破坏,Oracle在系统中锁定不同的数据库资源。数据库锁防止影响另外一些人使用数据库。

锁可分为自动锁和显示锁,当进行数据库操作时,缺省情况下,一个事务会自动获得所有数据库资源所需要的锁。例如,假设开始一项新事务,要更新一个客户的地址,在更新需要的记录行之前,Oracle进行检查,确定有没有其他事务锁定这行记录。如果有其他事务锁定这条记录,Oracle则等待,直到其他事务释放了该记录的锁;如果没有其他记录锁定该记录,则自动为该记录上锁,到该事务提交或回滚这个事务时才释放该锁。

Oracle的缺省锁机制对大多数应用而言已经足够。但也有一些情况下,事务需要显示的锁定数据库操作需要用到的数据。例如,如果要对表中大多数记录行进行更新,而表又很大,那么对整个表锁定比对一行记录锁定更有效。而且,对表进行锁定可以保证对表更新一次完成,中间不需要有锁等待。

Oracle的锁根据对象的级别可以分为表级锁、行级锁等。

2.9.1 锁级别

通常,Oracle有两种锁锁定数据资源:共享锁和排他锁。图2-30显示了共享锁和排他锁的使用。

图2-30 共享锁和排他锁的使用

1.共享锁

数据库资源的共享锁给予一个事务对特定锁资源的共享访问,同时另一事务也可以获得同一资源的共享锁。例如图2-30中,两个事务都具有同一表的共享锁,这就允许不同事务在同一时间更新同一表中不同的记录行。

共享锁允许事务高度并发性,又称共享锁为写锁。然而,事务不能总是为所有类型的资源与操作获得共享锁。例如,图2-30中虽然每个事务都可以同时获得同一个表的共享锁,但该事务对它所更新的行获得排他锁,使其他事务不能更新相同的行,以保护数据安全。

2.排他锁

排他锁不同于共享锁的是,排他锁在锁定了某资源后,不允许其他事务获取该资源,例如图2-30中,事务1获得第1行与第2行的排他锁后,另一事务2不可以在事务1提交或回滚前获得相同行的共享锁或排他锁。

排他锁显然比共享锁更严格,排他锁又叫写锁,并发性程度更低。因而,Oracle不自动获取数据库资源的排他锁,除非个别需要使用,如需阻止其他并发事件的破坏性交互。

2.9.2 DML锁

当事务进行DML操作(INSERT、UPDATE、DELETE)时,Oracle自动获得数据库中表与索引的锁。为提高并发性并且防止破坏数据,在DML执行过程中,Oracle可以同时获得行级锁和表级锁。

1.行级锁

当事务插入、更新、删除行时,该事务自动获得该特定行的排他锁。在行级锁锁数据行期间,其他事务不可以对该行更新、删除或修改。举一个例子,当事务中含有以下语句时,Oracle将自动锁定CUSTOMERS表中的行。

        SQL>UPDATE sales.customers
        SET….
        WHERE last_name='Ellison' AND first_name = 'Lawrence';

如果其他事务在以上事务提交或回滚之前,想更新CUSTOMERS表中的Larry Ellison的记录,Oracle发现该行已被锁定,就会让后来的事务处于等待状态。

在更新记录之前,事务可以使用SELECT…FOR UPDATE语句先行锁定要更新的记录。例如,以下语句将所有的zipcode为95000的记录锁定。

        SQL>SELECT * FROM sales.customers
        WHERE zipcode=95000
        FOR UPDATE
        NOWAIT;

当执行以上语句时,如果不能锁定所有满足条件的记录,Oracle会返回控制。如果没有NOWAIT关键字,SELECT…FOR UPDATE语句就会一直处于等待状态,直到获得所有的记录行。

2.表级锁

当事务获得一条记录上的锁时,事务自动获得含该行的表的锁。当事务更新表中一行或多行时,可以使用表级锁,以防止其他DDL操作破坏表更新。

例如,要更新CUSTOMERS表中的一行时,事务获得更新行的排他锁,同时也获得CUSTOMERS的表级锁,将表锁定以防止其他事务修改或删除表。

事务可以在事务进行过程中获得表上的共享锁或排他锁。当事务执行一条基本的DML操作,如INSERT、UPDATE或DELETE时,事务通常给共享锁,但也可以使用排他锁。这时需要用LOCK TABLE在表中显示的指定。例如以下语句就用LOCK TABLE语句获得CUSTOMERS的排他锁。

        SQL>LOCK TABLE customers
        IN EXCLUSIVE MODE
        NOWAIT;

如果Oracle不能立即获得所需要的表级锁,NOWAIT会将控制返回,告知不能立即获得表级锁。如果没有NOWAIT,事务将一直处于等待状态,直到获得表级锁。

Oracle实际上还有几种不同级别的表级锁,这里不再详细介绍,包括行共享、行排他、共享行排他以及排他级别,每个表级锁都比前一个严格。

3.死锁

死锁是Oracle系统应当尽量避免的情形,它是因为资源共享而引起的。产生的原因是两个或多个事务等待需要的某个资源,而又不释放现有的资源。图2-31 示意的是死锁发生的过程。

图2-31 死锁的产生

图2-31中,事务1在表的Row #1有一个排他锁并等待事务2释放#2的排他锁,但同时#2具有#1需要的排他锁并且在等待#1释放锁。因而两个事务一直僵持,如果没有外来干预,两个事务将永远僵持下去。

死锁的发生是因为不合理的设计。例如,以下两个事务,每个事务含有对PARTS表的多个更新,结果处于死锁状态,因为它们各自锁定了另一个事务需要的行,如表2-7所示。

表2-7 死锁的两个事务

上述事务的设计容易引起死锁,当事务1和事务2同时执行时,事务1需要锁定id=2的行,而事务2将id=2的行已先行锁定,事务1只能等待事务2释放锁;而事务2又在等待事务1的锁,两个事务进入死循环。我们可以对表2-7加以改进,避免死锁,如表2-8所示。

表2-8 改进后的两个事务

上述表对事务设计进行了改进,在每一个UPDATE语句后面都加COMMIT语句,以及时提交并释放行级锁,这样避免了死锁。Oracle在运行过程中会自动检测死锁,并回滚产生死锁的语句以消除死锁。

2.9.3 DDL锁

前面解释了Oracle自动用于保护DML操作的锁。但在使用CREATE、ALTER与DROP语句时,Oracle也会自动锁定数据。每个DDL操作在它自己的事务中完成,只有在进行DDL操作期间才会使用DDL锁。

1.排他DDL锁

创建、修改或删除数据库对象的DDL语句需要对目标对象使用排他锁。例如,当执行一个ALTER TABLE语句时,要给表増加完整性约束,事务会自动给表加排他锁。在ALTER语句前,其他用户不能修改或删除该表。

2.共享DDL锁

一些DDL语句可以获得数据库对象的共享DDL锁,在数据库对象之间建立相互依赖关系的DDL语句通常需要共享DDL锁。例如,创建包时,包的过程与函数引用许多不同的数据库表:当创建这个包时,事务获得这个包上的排他DDL锁,同时获得所引用锁的共享DDL锁,这个共享DDL锁阻止另一个事务获得所引用表的排他锁,而且防止Oracle在完成包编译前修改或删除包所引用的表。和DML共享锁一样,DDL共享锁不妨碍另一个事务获得同一表的共享DDL锁。

2.10 SQL语句的处理过程

Oracle数据库是关系数据库,Oracle用SQL语言对数据库进行操作。了解SQL语句的处理过程,能更深一步地了解Oracle的内部运行机制。

2.10.1 SQL语句的处理过程

Oracle中,所有的SQL语句都分三个阶段进行处理:语法分析、执行、返回结果。无论何种工具(如:Oracle Form 、Oracle Reports),都要将语句传递到Oracle进行处理。下面我们简单分析这三个阶段。

2.10.2 分析

分析是处理SQL语句的第一步,SQL语句从用户进程送到服务器进程,服务器进程开始进行以下工作。

(1)语法分析。Oracle采用自底向上的分析方法,检查语句是否符合语法规范,命名是否符合命名规范。语法分析是处理SQL语句过程中最费时间且代价最高的。

例如前面提到的如下SQL语句。

        SQL>SELECT ENAME,SAL FROM EMP;

服务器进程会将其中的关键字SELECT、FROM,表名EMP,列名ENAME、SAL分析出来等待语义分析。

(2)语义分析。语法分析通过,说明SQL语句格式正确,但还不知道语句中的对象在数据中是否存在,当前用户是否有权限读写。语义分析的功能就是验证这些。它根据语法分析出来的各数据库对象,分别从数据字典中取出其定义和权限。

如前面例中的表名EMP,先从数据字典中看是否有该表,如果有,就从数据字典中取出表EMP的定义,查看当前用户是否有权限读,否则提示出错。

(3)视图转换,将涉及视图的查询语句转换为相应的对基表查询语句。

(4)表达式转换,将复杂的SQL表达式转换为较简单的等效连接表达式。

(5)选择优化器。不同的优化器一般产生不同的“执行计划”。

(6)选择连接方式。Oracle有三种连接方式,对多表连接,Oracle可选择适当的连接方式。

(7)选择连接顺序。对多表连接,确定Oracle选择哪一对表先连接,选择这两表中哪个表作为源数据表。

(8)选择数据的搜索路径。Oracle根据以上条件选择合适的数据搜索路径,例如是选用全表搜索还是利用索引或是其他的方式。

(9)到SGA中为该SQL语句找到一个共享SQL区。

如果在共享SQL区中已有该SQL语句的共享SQL区,则Oracle直接执行共享SQL区内容,而不进行以上分析。

2.10.3 执行

执行阶段执行已分析过的语句。如果SQL语句会改变数据库,如UPDATE、DELETE语句,Oracle先要将修改的行锁住,以防其他用户修改;如果SQL语句不会改变数据库,则执行下一步的数据读。

Oracle会先从数据库缓冲区中寻找是否有所要的数据块,如果有,就直接读或修改;否则从物理文件中读到数据库缓冲区。

2.10.4 返回结果

对于SELECT语句等需要返回结果的语句,还有返回结果阶段,将执行结果,如SELECT后的所有数据行,返回给用户进程。如果查询需要排序,则Oracle将排序的结果返回给用户。查询结果总是以表格形式出现,根据使用的内存大小不同,Oracle可以一次取出一行数据,也可以一次取出一组。

2.10.5 SELECT语句的处理步骤

在DML类型的SQL语句中,SELECT语句是数据库中最常使用的命令,图2-32列出了处理查询语句的具体步骤。

(1)创建游标(Cursor)。游标可以是显式的,也可以是隐式的。

(2)分析语句。

(3)定义输出,指定位置、类型和结果集的数据类型,转换数据类型。

(4)捆绑变量,如果查询语句中有变量值,需取到变量的值。

(5)判断是否能并行查询。

(6)执行查询。

(7)以行方式取出数据。

(8)关闭游标。

图2-32 处理SELECT语句过程

2.10.6 其他语句的处理步骤

SELECT以外的其他语句和SELECT执行过程稍有不同,其他语句不需返回结果,如图2-33所示。

图2-33 其他SQL语句处理过程

(1)创建游标(Cursor),使用隐式游标。

(2)分析语句。

(3)捆绑变量,如果查询语句中有变量值,需取到变量的值。

(4)看是否能并行查询。

(5)执行语句。

(6)通知用户执行完成。

(7)关闭游标。

2.10.7 SCN的运行机制

在介绍Commit和Rollback的处理之前,我们先讨论一个与数据库恢复有关的重要机制:系统改变号SCN(System Change Number)。

SCN是数据库中非常重要的一个数据结构。它定义数据库在某个确切时刻提交的版本。每当事物被提交时,它被赋予一个唯一标识事务的SCN。SCN提供Oracle的内部时钟机制,可被看作逻辑时钟。这对于恢复操作是至关重要的,Oracle只根据SCN执行恢复。SCN用来同步数据库,并且提供数据读的一致性。执行查询语句时,Oracle在执行阶段就确定了当前SCN,只有SCN号小于或等于当前的SCN号,数据块才能读取。对于较高的SCN号,Oracle就从回滚段中读取。

SCN记录在控制文件、数据文件头部、块的头部以及重做日志文件中。对同一个事务,重做日志文件存了低的SCN号和高的SCN号。

每个事务提交时都会增加数据库系统的SCN号。但查询开始时,Oracle执行下述操作,为查询返回集产生一个读一致性数据集。

(1)系统查询时,Oracle查看系统当前的SCN,这里称为查询SCN。

(2)在Oracle查询时,它必须读数据块以建立查询返回集。对于要读的每一个数据块,Oracle将查询SCN与数据块头的SCN相比较,然后做如下处理。如果数据块的SCN等于或先于查询SCN,Oracle可以使用块中的数据创建查询结果集;如果数据块中的SCN大于查询SCN,Oracle从系统回滚段读信息,即重新生成一个数据块。

图2-34展示了一个多版本返回与查询SCN一致性的示意图。

图2-34 SCN控制读一致性

2.10.8 Commit的处理

数据库中涉及到事务时,经常会遇到提交(Commit)操作。当用户发出提交命令Commit后,处理Commit的步骤如下。

(1)服务器进程先生成一个SCN号,赋给回滚段(可参考2.6.4节看回滚段定义),在回滚段中作标志表示事务已提交。服务器进程将提交的记录和SCN号存到重做日志缓冲区中,同时将数据库缓冲区作标志。

(2)LGWR进程将重做日志缓冲区中包含提交记录及SCN号写到联机重做日志文件中。

(3)服务器进程解开对表和行的锁定。

(4)通知用户Commit已经完成。

(5)服务器进程将事务标记为完成。

Commit进行时,DBWR并不立刻写磁盘,Oracle会延迟向数据文件中写已修改的数据。这就是所谓的“快速提交机制”。快速提交的优点有以下几个。

❑ LGWR往日志文件上写是顺序写,比DBWR向数据文件中不同块写要快。

❑ LGWR往日志文件上写的内容要比向数据文件上写的内容少。日志文件只需存变动的内容,而数据文件上要存整个数据块内容。

❑ 如果多个事务发数据库提交命令,LGWR会将这些提交信息一起往日志文件上写。

❑ 一般一个事务最多有一次写日志文件,除非重做日志文件写满。

❑ 事务的大小不会影响Commit操作的时间。

2.10.9 Rollback回滚的处理

如果事务没有提交,就可以回滚,也就是可以恢复到原先的状态。发生以下情况Oracle会进行回滚。

❑ 用户发出Rollback命令。

❑ 服务器异常结束。

❑ DBA停止会话。

以下是处理Rollback的步骤。

(1)通过回滚段,恢复在事务中所做的所有修改。

(2)服务器进程释放所有的对表和行的锁定。

(3)服务器将事务标记为完成。

2.11 Oracle数据字典

数据字典内存放了Oracle的数据库信息,用户可以通过数据字典来查看这些信息。数据字典是只读的,它是Oracle数据库的最重要的部分之一,是由一组只读的表及其视图组成。数据字典可提供以下的信息。

❑ Oracle用户的名字

❑ 每一个用户所拥有的权限和角色

❑ 数据库对象的名字(表、视图、快照、索引、聚集、同义词、序列、过程、函数、包及触发器等)

❑ 关于完整性约束的信息

❑ 列的缺省值

❑ 有关数据库中对象的空间分布及当前使用情况

❑ 审计信息(如谁存取或修改各种对象)

❑ 其它的一些数据库信息

Oracle中的数据字典有静态和动态之分。静态数据字典主要包含用户在访问数据字典时不会发生改变的信息。而动态数据字典是依赖数据库运行的性能的,反映数据库运行的一些内在信息,所以在访问这类数据字典时数据往往不是一成不变的,数据库管理员常从动态数据字典中获得数据的运行信息。接下来我们分别就这两类数据字典来论述。

2.11.1 静态数据字典

这类数据字典主要是由表和视图组成。应该注意的是,数据字典中的表是不能直接来访问的,但是可以访问数据字典中的视图。静态数据字典中的视图分为三类,它们分别为:USER_、ALL_、DBA_。

❑ USER_视图存储了当前用户所拥有的对象的信息(即所有在当前用户模式下的对象)。

❑ ALL_视图存储了当前用户能够访问的所有对象的信息。与USER_相比,ALL_并不需要拥有该对象,只需要有访问该对象的权限即可。

❑ DBA_视图存储了数据库中所有对象的信息。当前用户必须具有访问这些数据库的权限才能访问,一般来说必须具有管理员权限。

三类视图之间的数据有重叠,它们之间除了因为访问权限不一样所以访问范围不一样之外,其他均具有一致性。具体来说,由于数据字典视图是由SYS(系统用户)所拥有的,在缺省情况下,只有SYS和拥有DBA系统权限的用户可以看到所有的视图。没有DBA权限的用户只能看到USER_和ALL_视图。如果用户没有被授予相关的SELECT权限,就不能看到DBA_视图的内容。举例来说,USER_USERS、ALL_USERS、DBA_USERS视图内放的都是有关用户的信息,USER_USERS内放的是用户名,用户状态,用户创建时间等信息,而ALL_USERS内放的是当前数据库内所有用户的用户名称和用户创建时间。DBA_USERS内描述的字段信息和USER_USERS一样,但显示所有用户的信息。三者一般同时出现,有USER_USERS就有DBA_USERS和ALL_USERS;同样,有USER_TABLES就有DBA_TABLES和ALL_TABLES等,只是范围不一样。

下面我们以USER_为例介绍几个常用的静态视图。

1.USER_USERS视图

该视图显示当前用户的信息,主要包括当前用户名、账户id、账户状态、表空间名、创建时间等。

2.USER_TABLES视图

该视图显示当前用户拥有所有表的信息,主要包括表名、表空间名、簇名等。通过此视图用户可以清楚了解当前用户可以操作的表。

3.USER_OBJECTS视图

该视图显示当前用户拥有的所有对象的信息,对象包括表、视图、存储过程、触发器、包、索引、序列等。该视图比USER_TABLES视图更加全面。例如,如需要获取一个名为“package1”的对象类型和其状态的信息,执行以下命令。

        SQL>select object_type,status
        from user_objects
        where object_name=upper('package1');

这里upper(一个PL/SQL内置函数)函数用来转换大小写,数据字典里的所有对象均为大写形式,而PL/SQL里不区分大小写,在实际操作中一定要注意大小写匹配。

4.USER_TAB_PRlVS视图

该视图显示当前用户对所有表的权限信息。

例如,为了了解当前用户对table1的权限信息,执行以下命令。

        SQL>select *  from user_tab_privs
        where table_name=upper('table1')

了解了当前用户对该表的权限之后用户就可以清楚地知道,哪些操作可以执行,哪些操作不能执行。

2.11.2 查看各类静态对象

查看各类静态对象内容对分析Oracle的内部运行机制有十分重要的作用,有时开发人员也需要如何查看各类静态对象。下面我们将给出查看一些最常用对象的方法。

1.查看用户信息

查看当前用户的缺省表空间的代码如下。

        SQL>select username,default_tablespace
        from user_users;

查看当前用户的角色的代码如下。

        SQL>select *  from user_role_privs;

查看当前用户的系统权限和表级权限如下。

        SQL>select * from user_sys_privs;
        SQL>select * from user_tab_privs;

2.表

查看用户下所有的表的代码如下。

        SQL>select * from user_tables;

查看名称包含log字符的表的代码如下。

        SQL>select object_name,object_id
        from user_objects
        where instr(object_name,'LOG')>0;

查看某表的创建时间的代码如下。

        SQL>select object_name,created
        from user_objects
        where object_name=upper('&table_name');

查看某表的大小的代码如下。

        SQL>select sum(bytes)/(1024*1024)as "size(M)"
        from user_segments
        where segment_name=upper('&table_name');

查看放在Oracle的内存区里的表的代码如下。

        SQL>select table_name,cache
        from user_tables
        where instr(cache,'Y')>0;

3.索引

查看索引个数和类别的代码如下。

        SQL>select index_name,index_type,table_name
        from user_indexes
        order by table_name;

查看索引被索引的字段的代码如下。

        SQL>select *
        from user_ind_columns
                where index_name=upper('&index_name');

查看索引的大小的代码如下。

        SQL>select sum(bytes)/(1024*1024)as "size(M)"
        from user_segments
        where segment_name=upper('&index_name');

4.序列号

查看序列号,last_number是当前值的代码如下。

        SQL>select * from user_sequences;

5.视图

查看视图的名称的代码如下。

        SQL>select view_name
        from user_views;

查看创建视图的select语句如下。

        SQL>set view_name,text_length
        from user_views;
        SQL>set long 2000;      %说明:可以根据视图的text_length的值设定set long的大小
        SQL>select text
        from user_views
        where view_name=upper('&view_name');

6.同义词

查看同义词的名称的代码如下。

        SQL>select * from user_synonyms;

7.约束条件

查看某表的约束条件的代码如下。

        SQL>select constraint_name, constraint_type,search_condition, r_constraint_name
            from user_constraints
            where table_name = upper('&table_name');
        SQL>select c.constraint_name,c.constraint_type,cc.column_name
              from user_constraints c,user_cons_columns cc
              where c.owner = upper('&table_owner')and c.table_name = upper('&table_name')
              and c.owner = cc.owner and c.constraint_name = cc.constraint_name
              order by cc.position;

8.存储函数和过程

查看函数和过程的状态的代码如下。

        SQL>select object_name,status from user_objects where object_type='FUNCTION';
        SQL>select object_name,status from user_objects where object_type='PROCEDURE';

查看函数和过程的源代码如下。

        SQL>select text from all_source where owner=user and name=upper('&plsql_name');

2.11.3 动态数据字典

Oracle包含了一些潜在的由系统管理员如SYS维护的表和视图,因为数据库运行时它们会不断进行更新,所以称为动态数据字典(或者是动态性能视图)。这些视图提供了关于内存和磁盘的运行情况,所以只能对其进行只读访问而不能修改。

Oracle中这些动态性能视图都是以v$开头的视图,比如v$access。下面我们就几个主要的动态性能视图进行介绍。

1.V$ACCESS视图

视图内描述了数据库中锁定的数据库对象以及访问这些对象的会话对象(session对象)。

2.V$SESSlON视图

视图内描述了当前会话的详细信息。该视图字段较多。

3.V$ACTlVE_lNSTANCE视图

视图内描述了当前数据库下活动实例的信息。

4.V$CONTEXT视图

视图内描述了当前会话的属性信息,比如命名空间、属性值等。

2.11.4 各类数据字典区分

Oracle中有多种数据字典,有ALL_,USER_,DBA_,V$,GV$,V_$,GV_$,X$。数据字典之间的关系比较复杂,前四种前面我们已介绍过,下面详细介绍后几种。

GV$视图是从Oracle 8开始引入的,G表示Global。除了一些特例以外,每个V$视图都有一个对应的GV$视图存在,如GV$FIXED_TABLE对应V$FIXED_TABLE视图。GV$视图出现在并行服务器(OPS)环境中。在并行服务器环境中,GV$视图返回的是所有实例信息;而V$视图则是在GV$视图的基础上,增加了实例ID判断后的结果,即每个V$视图都是在GV$视图中包含语句where inst_id = USERENV('Instance')而产生的,如图2-35所示。

图2-35 数据字典关系图

V$、GV$实际上又是GV_$,V_$视图的同义词(图2-35中以‘=’号表示),V$,GV$是用下面SQL语句从GV_$,V_$视图中创建的。

        SQL>create or replace view v_$nls_parameters as select * from v$nls_parameters;
        SQL>create or replace public synonym v$nls_parameters for v_$nls_parameters;
        SQL>grant select on v_$nls_parameters to public;

事实上,V$视图是真正的视图,V$视图是基于X$表直接建立的。

X$表是Oracle数据库的运行基础,在数据库启动时由Oracle应用程序动态创建。

2.11.5 动态数据字典结构

以后用户经常会用到动态数据字典,尤其是数据管理员。动态数据字典是管理员获取数据库信息的一个接口。图2-36、图2-37和图2-38给出动态数据字典的结构和分类,以帮助读者加深理解。

图2-36 与日志相关的动态视图结构

图2-37 与数据实例相关的动态视图结构

图2-38 与物理文件读取相关的动态视图结构

2.11.6 动态数据字典分类

Oracle数据库中保存了许多动态数据字典,为了便于用户掌握,这里专门对这些数据字典进行分类,表2-9给出一个数据字典分类表。

表2-9 数据字典分表

续表

续表

说明:“支持的数据库版本”列如果没有写出支持的版本,表示任何版本都支持。