Oracle Database 11g 面向 DBA 和开发人员的重要特性

新进程

每个新版本的 Oracle Database 中都会引入一组新进程的新缩写。下面是 Oracle Database 11g 中的新进程缩写列表:

进程

名称

描述

ACMS

内存服务器原子控制文件

仅适用于 RAC 实例中。执行分布式 SGA 更新时,ACMS 可确保在所有实例上发生更新。如果某个实例上的更新失败,此进程也会在所有实例上回滚更新。可将 ACMS 视为两阶段提交协调器进程,用于 RAC 集群中的 SGA 更新。

DBRM

数据库资源管理器

执行资源计划和其他与资源管理器相关的任务

DIA0

诊断进程 0

检测挂起情况和死锁。将来可能会引入多个进程,因此将名称设置为 diag0。将来如果引入其他进程,将分别命名为 dia1、dia2 等。

DIAG

诊断进程

执行诊断、转储跟踪文件(如有必要)以及执行全局 oradebug 命令

FBDA

闪回数据存档器

Oracle Database 11g 提供新的“闪回存档”,用于写入对表所作的更改(请参阅本系列的“事务管理”部分)。此进程将写入闪回存档。

GTX0

全局事务进程 0

在 RAC 集群中,Oracle Database 现在提供改进的 XA 事务处理。此进程协调 XA 事务。如果 XA 上的数据库负载增长,将自动创建更多进程,分别命名为 GTX1、GTX2,直至 GTXJ。

KATE

ASM 临时任务处理器

毫无疑问,这是所有新进程中最长的名称。它是在 ASM 实例上出现的,而不是在数据库上出现。(有关新特性“快速磁盘重新同步”的信息,请参阅本系列中有关 ASM 的部分。)当磁盘脱机时,此进程将代替磁盘对 ASM 元文件执行代理 I/O。

MARK

为重新同步协调器标记分配单元

有关 ASM 磁盘组弹性的更多详细信息,请参阅 ASM 部分。当磁盘出现故障时将会脱机,从而导致写入内容丢失。在这种情况下,此进程会将 ASM 分配单元 (AU) 标记为陈旧。当磁盘联机时,陈旧的段将被刷新。

SMCO

空间管理器

SMCO 是主空间管理进程,可动态分配和回收空间。它将生成从进程 Wnnn 以执行这些任务。

VKTM

虚拟计时器进程

提供时钟时间(每秒钟更新一次)。以提升的优先级运行时,此进程每 20 毫秒更新一次。

W000

空间管理工作器进程

执行从 SMCO 收到的指令。根据需要生成更多进程,命名为 W000、W001 等。

 

着色的 SQL

此新特性不会将 SQL 语句涂上颜色,而是类似于将其标记为“重要”。

假设您正在对某个性能问题进行故障诊断,您怀疑该问题是由某条 SQL 语句导致的。您希望在每个自动负载信息库 (AWR) 快照中捕获该 SQL 语句。但 AWR 快照并不捕获所有 SQL 语句,而只捕获前几条语句。如何强制快照捕获特定的 SQL,而不管它是否包括在前几条 SQL 语句?

程序包 dbms_workload_repository 中的 add_colored_sql() 过程将 SQL 标记为“着色的”,或者将其标记为重要,应该在每个 AWR 快照中捕获该 SQL,而不管它是否包括在前几条 SQL 语句中。首先确定 SQL 语句,然后获取其 SQL_ID。要对该语句着色,请使用以下命令:

  
begin
  dbms_workload_repository.add_colored_sql(
     sql_id => 'ff15115dvgukr' 
  );
end;

要找出已着色的 SQL,可以查询 AWR 表 WRM$_COLORED_SQL:

SQL> SELECT * FROM wrm$_colored_sql;

DBID  SQL_ID  OWNER      CREATE_TI
---------- ------------- ----------      --------
2965581158 ff15115dvgukr 1 05-APR-08


之后,将在每个快照中捕获 ID 为 ff15115dvgukr 的 SQL 语句,即使该语句不包括在前几条 SQL 语句中。(当然,该 SQL 必须位于库缓存中才能在 AWR 快照中捕获它。)

但是,如果 SQL 不再需要着色(即,该语句不再重要,没有必要捕获它)时怎么办呢?可以通过反向执行该过程来取消着色。

begin
  dbms_workload_repository.remove_colored_sql(
    sql_id => 'ff15115dvgukr' 
  );
end;

当您要在调优活动中关注某个 SQL 时,此特性极为有用。


COPY 仍然存在!

与传言相反,COPY 命令仍然存在。并且它仍然是复制 LONG 数据类型的唯一方式。但由于此命令已被弃用,因此它已经跟不上 Oracle Database 的发展步伐。

例如,它不知道存在新的数据类型 BFILE。如果您尝试复制包含 BFILE 类型列(如先前的示例中所示)的 DOCS 表:

SQL> copy to sh@oradba11 -
> replace docs_may08 -
> using select * from docs
> where creation_dt between '1-may-08' and '31-may-08'; 
Enter TO password: 
 
Array fetch/bind size is 15. (arraysize is 15)
Will commit when done. (copycommit is 0)
Maximum long size is 80. (long is 80)
 
CPY-0012: Datatype cannot be copied

错误消息描述的是:COPY 命令无法复制 BFILE 数据类型。但如果表中包含 Long 类型列,则无法使用 INSERT /*+ APPEND */ 和 CREATE TABLE ...NOLOGGING AS SELECT ... 语句。在以下示例中,我们尝试将一个表中的某些行复制到另一个表中,第二个表中有一个定义为 LONG 类型的列:

SQL>create table mytab1 as select * from mytab where col1 = 'A';

create table mytab1 as select * from mytab
                              *
ERROR at line 1:
ORA-00997: illegal use of LONG datatype
 
 
SQL> insert /*+ APPEND */ into mytab 
  2> select * from mytab where col1 = 'A';
select * from mytab
       *
ERROR at line 2:
ORA-00997: illegal use of LONG datatype

因此,要复制大量数据而不填充撤销段,唯一简单的方法就是使用 COPY 命令。


Export 和 Import:濒临废弃

在 Oracle Database 10g 中引入 Data Pump 时,预计最终将废弃传统的 Export 和 Import 工具。那么,请继续等待:在 Oracle Database 11g 中,这些旧工具仍然存在。由于已被弃用,这些工具不会再得到任何增强,但绝不能说它们是无用的。

就功能而言,Data Pump 胜过常规 Export/Import 许多倍,但在一种简单情况下,后者仍然非常有用:当您必须在客户端而不是服务器上创建转储文件时。由于执行操作前不必创建目录,因此在许多方面,通过常规 Export/Import 执行简单的数据导出可能更容易。

但是,需要再次声明的是,由于这些工具已被弃用,因此在使用时存在以下三个风险:

  • Oracle 不再为这些工具添加任何功能,因此可能不支持将来的数据类型等。
  • 这些工具可能会在未经事先声明的情况下从将来的版本中删除,您以后必须重写代码。
  • 如果您遇到错误,Oracle 支持部门可能会拒绝修复该代码。
因此,可能有必要将现有代码转换为 Data Pump,并且为了适应新发展,也确实应该这样做。


增量更新的全局统计信息

在分区表中,每个分区都可以有优化器统计信息。此外,在整个表上还有与分区无关的全局统计信息,例如,整个表中不同值的数量。仅在 dbms_stats.gather_*_stats 程序发出指令时才会收集此全局统计信息,默认情况下不会收集。如果未指定全局统计信息参数,则不会收集全局统计信息,优化器将根据分区统计信息生成全局统计信息。当然,计算得到的全局统计信息始终要比生成的统计信息精确,因此始终建议您收集全局统计信息。

但是,过去一直存在一个小问题:当分区数据变化但其他分区保持不变时,全局统计信息可能会变得陈旧。因此,即使只有一个分区发生变化,统计信息收集程序也必须遍历整个表来收集全局统计信息。

现在这个问题已不复存在。在 Oracle Database 11g 中,可以指示优化器仅从更改的分区中以增量方式收集全局统计信息,而不必再次执行整表扫描。通过将用于统计信息收集的表参数 INCREMENTAL 设置为 TRUE 可完成此操作,如下所示:

begin
   dbms_stats.set_table_prefs(
           'SH','SALES','INCREMENTAL','TRUE');
end;
/

现在,您应该专门收集此表中粒度为 AUTO 的分区 SALES_1995 上的统计信息。

begin
   dbms_stats.gather_table_stats (
           ownname             => 'SH',
           tabname         => 'SALES',
           partname        => 'SALES_1995',
           granularity     => 'AUTO'
   );
end;

此统计信息收集方法指示数据库根据从新分区收集的统计信息,以增量方式更新全局统计信息。


Data Pump 功能改进

正如我先前提到的,自上一个版本开始,Data Pump 工具一直用于移动大量数据,或者对数据进行有效的“逻辑”备份。与 Export/Import 类似,它是独立于平台的工具(例如,可以从 Linux 导出,然后再导入 Solaris)。在 Oracle Database 11g 中,Data Pump 获得了一些增强。

Data Pump 存在的较大的一个问题是,创建转储文件时无法对其进行压缩。在较老的 Export/Import 实用程序中,这一点比较容易做到。在 Oracle Database 11g 中,Data Pump 可以在创建转储文件时对其进行压缩。这是通过在 expdp 命令行中使用参数 COMPRESSION 实现的。此参数有三个选项:

  • METDATA_ONLY — 仅压缩元数据;数据保持原样(在 Oracle Database 10.2 中同样可用)。
  • DATA_ONLY — 仅压缩数据;元数据保持原样。
  • ALL — 同时压缩元数据和数据。
  • NONE — 此为默认选项;不执行任何压缩。
下面说明如何压缩 UNITS_FACT 表的导出内容:
$ expdp global/global directory=dump_dir tables=units_fact dumpfile=units_fact_comp.dmp compression=all

为了便于比较,下面说明如何只导出而不压缩:
$ expdp global/global directory=dump_dir tables=units_fact dumpfile=units_fact_uncomp.dmp

现在,如果您查看所创建的文件:
$ ls -l 
-rw-r-----  1 oracle dba  2576384 Jul  6 22:39 units_fact_comp.dmp
-rw-r-----  1 oracle dba 15728640 Jul  6 22:36 units_fact_uncomp.dmp

压缩比例为 100*(15728640-2576384)/15728640,大约为 83.61%!这是一个相当大的压缩比例;解压缩的转储文件为 15MB,而压缩后的转储文件为 1.5MB。

如果使用 gzip 压缩转储文件:

$ gzip units_factl_uncomp.dmp

-rw-r-----  1 oracle dba 3337043 Jul  6 22:36 units_fact_uncomp.dmp.gz

压缩后的文件大约为 3.2MB,其大小是 Data Pump 中压缩文件的二倍。因此,除了压缩可能会更加高效,解压缩也真正带来了好处。导入转储文件时,导入不必先解压缩文件;它将在读取文件时进行解压缩,从而使此过程真正变快。

Data Pump 中的其他两项增强功能如下:

  • 加密:可在创建转储文件时对其进行加密。加密与 TDE(透明数据加密)使用相同的技术,并使用钱夹存储万能密钥。与 Oracle Database 10g 中不同,此加密将在整个转储文件上发生,而不仅仅在加密的列上发生。
  • 屏蔽:将数据从生产导入 QA 时,您可能希望确保将敏感数据(如社会保险号码等)变为乱码(将其修改成不可识别的内容)。Oracle Database 11g 中的 Data Pump 可以完成此操作,方法是创建一个屏蔽函数,然后在导入期间使用此函数。


可以在安全性部分中了解这两个特性。
 

Data Pump 旧模式(仅限第 2 版)

如果您是一位经验丰富的导出/导入(原先的 Export,即“exp”工具)用户,可能已使用正确(并且通常仔细进行了优化)的参数细致地准备了 exp/imp 脚本。但是,现在不再支持原先的导出!因此,您要转向 Data Pump;但是您所钟爱并为之付出心血的脚本该怎么办?在您的脚本中,您可能希望用 expdp 代替 exp;但是这并不像替换可执行文件那么简单。所有参数该怎么办?

例如,请考虑原有的 exp 参数“owner”,它执行特定用户的所有对象的导出。expdp 对应的参数不是 owner;而是“schema”。因此,不仅要将“exp”替换为“expdp”,而且还要替换所有参数 — 这项任务不仅耗时而且很容易出错。

可怜可怜我吧。在第 2 版中,有一种特殊模式的 Data Pump,可处理原来的“exp”实用程序参数。我们来考虑以上问题。原来的脚本如下:

$ exp \"/ as sysdba\" rows=n owner=sh


不必将参数“owner”替换为“schema”,只需在 expdp 中运行以下脚本:

 
$ expdp \"/ as sysdba\" rows=n owner=sh


输出如下:

Export: Release 11.2.0.1.0 - Production on Sat Sep 26 19:02:25 2009 Copyright (c) 1982, 2009, Oracle and/or its affiliates.All rights reserved.Connected to: Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production With the Partitioning, Automatic Storage Management, OLAP, Data Mining and Real Application Testing options Legacy Mode Active due to the following parameters:
Legacy Mode Parameter: "owner=sh" Location: Command Line, Replaced with: "schemas=sh"
Legacy Mode Parameter: "rows=FALSE" Location: Command Line, Replaced with: "content=metadata_only"
Legacy Mode has set reuse_dumpfiles=true parameter.
Legacy Mode has set nologfile=true parameter.

Starting "SYS"."SYS_EXPORT_SCHEMA_01": "/******** AS SYSDBA" content=metadata_only schemas=sh reuse_dumpfiles=true nologfile=true Processing object type SCHEMA_EXPORT/USER
… the rest of the output comes here …


注意该实用程序是如何自动检测与原有 exp 中相关的参数。该实用程序会轻松替换与 expdp 命令相关的参数(而不是投诉和终止执行并报告错误),如输出中的粗体部分所示。您的脚本不必更改。

现在,您知道该工具如何将“owner”自动替换为“schema”,可能有兴趣了解其他参数将受到什么影响。将忽略某些参数,例如“buffers”。原先的导出使用该参数,因为行是使用传统方式导出的。Data Pump 使用直接路径,因此,该参数现在无关紧要,因此被忽略。

大部分参数会自动转换为其 Data Pump 的对应参数。您之前看到了一个示例;另一个示例是原先 exp 中的“constraints=n”会在 Data Pump 中转换为“exclude=constraints”。该转换也很智能:会将 Data Pump 功能考虑在内。

请考虑原先导出中的“consistent=y”参数,该参数可导出截至本导出首次启动时存在的所有行。Data Pump 中的相应功能由参数“flashback”提供。但是,该参数不再取值“y”或“n”;而是需要一个时间戳。在转换期间,Data Pump 会记录下时间并针对 flashback 参数输入值。

其他在 Data Pump 中没有等效功能的参数将停止运行并报错。例如,原先导出中的“volsize”参数用于指定磁带设备的卷大小。由于 Data Pump 不支持磁带设备,该参数的存在将导致作业停止并报错。(要确定每个旧模式参数如何影响 expdp 和 impdp,请参阅实用程序指南 的第 4 章。)

谈到原先的 Export 实用程序时,您应注意一条告诫。是否还记得可以创建无段表?下面来温习一下:对于此类表,段是在插入第一行时即时创建的。在此之前,表没有任何分段。如果原先的导出没有发现分段,那么在导出期间会忽略这些表。如果只想单独导出元数据,请注意,转储文件将不会引用这些表。

当然,与时俱进的 Data Pump 没有此限制。导出时,它不会忽略无段表。
 

在 Data Pump 中利用集群(仅限第 2 版)

您知道,Data Pump 使用若干“从属进程”来并行执行工作。如果您有一个 RAC 数据库,有些时候,对所有从属进程使用数据库的所有实例(而非对所有从属进程只使用一个实例)可能更有帮助。命名相当恰当的新参数“cluster”可实现这一操作。该参数指定为

cluster=y


该参数的默认值为“y”。如果您要禁用该参数,应指定“n”。该参数可在 Data Pump Export 和 Import 中使用。

我们来考虑一种特殊情况。如果您有两个以上的实例,同时您希望以集群模式运行 Data Pump 从属进程,您希望该操作仅限于少数几个实例;而非所有实例,该怎么办呢?

这非常简单。您只需使用另一个参数 service_name。创建一个仅在要操作的实例上定义的服务名称,并在 Data Pump 中使用该服务名称。

以下是一个示例,其中有四个实例,您只希望针对实例 2 和 3 进行 Data Pump 作业。创建服务名称 nodes_2_3,如下所示:

$ srvctl add service -d <DatabaseName> -s nodes_2_3 -a RAC2,RAC3 -r RAC1,RAC4 -P BASIC


然后,您应在 Data Pump 中使用该参数:

 
cluster=y service_name=nodes_2_3

现在,Data Pump 作业将仅使用 RAC2 和 RAC3 实例,即节点 2 和 3。

要警惕一点:如果在节点 1 或 4 上启动 expdp 进程,那么 Data Pump 进程除了在节点 2 和 3 上启动外,也会在节点 1 或 4 上启动。与往常一样,在 RAC 数据库中以并行方式启动该 Data Pump 作业时,请确保在共享存储上创建转储文件,因为多个节点上的从属进程必须能够访问这些转储文件。

 

在 Data Pump 中禁用直接路径

默认情况下,Data Pump 作业使用直接路径插入(即 /*+ APPEND */ hint)以直接路径模式加载数据。虽然这样能够提高进程的加载速度,但它加载到表的高水位标记以上并锁定表,直至加载完成。此外,由于它始终加载新的块,因此,当转储文件仅包含少数几行时,这会浪费空间。

如果您要加载一个表,而该表还受 DML 活动的影响,该怎么办?您可通过选择一个新参数 data_options 来执行此操作,该参数可更改默认行为。以下是一个示例:

$ impdp dumpfile=sales.dmp tables=sales directory=input_dir data_options=disable_append_hint

disable_append_hint 值强制 Data Pump 作业通过正常插入(而不是直接路径插入)来加载。现在,DML 可在正在加载的表中运行。

该参数的另一个值是 skip_constraint_errors,即使非延迟约束上有约束违例,它也允许加载继续。该属性有助于加载全部表,然后通过 novalidate 子句一次性启用约束。该操作使其在数据仓库中非常有用。

 

真正联机的索引重建

是否记得在重建索引时使用的 ONLINE 子句?

alter index in_tab_01 rebuild online;

该子句可重建索引,而不影响访问它的 DML。它将跟踪正在访问的块,并在结束时使用新构建的索引合并这些块,从而完成索引重建。要完成此任务,操作必须在进程结束时获得独占锁定。虽然持续时间很短,但它仍然是一个锁定,DML 必须等待。


在 Oracle Database 11g 中,联机重建真正处于联机状态:它不进行独占锁定。DML 不会受影响。

使用不同的表空间存放临时表

创建全局临时表时,所占用的空间是从哪里分配的呢?它来自用户的临时表空间。通常这不会成为问题,但在某些特殊情况下,您可能要出于预期目的释放临时表空间(通常是为了排序)。有时,您可能要创建临时表,以使用位于更快、更高效磁盘上的其他临时表空间来提高数据访问速度。在这些情况下,只能将该表空间变成用户的临时表空间。

在 Oracle Database 11g 中,可以使用另一个临时表空间来存放全局临时表。让我们看一看这种方案的实现方式。首先,再创建一个临时表空间:

SQL> create temporary tablespace etl_temp
  2> tempfile '+DG1/etl_temp_01.dbf'
  3> size 1G;
  
Tablespace created.  

然后,使用新的表空间子句创建 GTT:
SQL> create global temporary table data_load (
  2>    input_line varchar2 (2000)
  3>  )
  4> on commit preserve rows
  5> tablespace etl_temp;

Table created.

现在,将在表空间 etl_temp 而不是用户的默认临时表空间 (TEMP) 上创建此临时表。

 

SQL*Plus 错误记录

假定您有一个名为 myscript.sql 的 SQL 脚本:

set puase on
set lines 132 pages 0 trimsppol on
select * from nonexistent_table
/

注意,此脚本中存在几处错误:第一行的“pause”和第二行的“trimspool”都存在拼写错误,而第三行的 select 语句所针对的是一个并不存在的表。在 SQL*Plus 提示符下运行该脚本时,除非您将输出假脱机,否则您之后将无法检查错误。即使假脱机输出,您也要访问物理服务器以检查假脱机文件,而这是不可能的。


Oracle Database 11g 提供了一个完美的解决方案:现在,您可以在一个专用表中记录来自 SQL*Plus 的错误。您应执行的第一条命令如下:

SQL> set errorlogging on

现在运行脚本:
SQL> @myscript

运行后将生成以下错误消息:
SP2-0158: unknown SET option "puase"
SP2-0158: unknown SET option "trimsppol"
select * from nonexistent_table
              *
ERROR at line 1:
ORA-00942: table or view does not exist

您可能见过这些消息,也可能没见过,这取决于脚本的运行方式 — 在前台通过 SQL*Plus 提示符运行,或者在后台作为脚本调用运行。运行完脚本之后,您可以登录到数据库并检查 SPERRORLOG 表中的错误。
sql> col timestamp format a15
sql> col username format a15
sql> col script format a10
sql> col identifier format a15
sql> col statement format a20
sql> col message format a20
sql> select timestamp, username, script, statement, message
  2> from sperrorlog;

注意,您是从 另一个 会话中检查错误,而不是从运行脚本的会话中检查。事实上,脚本已经完成,会话也已经结束。这是一项强大的功能,可以在 SQL*Plus 会话中检查发生的错误,如果没有这项功能,将无法(至少是很困难的)跟踪这些错误。


SPERRORLOG 表是专门针对此用途创建的。您还可以创建自己的表,并用 SQL*Plus 中的错误填充它。此表应按以下方式创建:

SQL> create table my_sperror_log
  2  (
  3     username varchar2(256),
  4     timestamp       timestamp,
  5     script          varchar2(1024),
  6     identifier      varchar(256),
  7     message         clob,
  8     statement       clob
  9  )
 10  /
 
Table created.

现在,您可以用此表代替默认表来记录错误。
SQL> set errorlogging on table sh.my_sperror_log;
SQL> @myscript

现在,MY_ERROR_LOG(而不是 SPERRORLOG)将保存错误日志。可以通过执行以下命令来截断所有行:
SQL> set errorlogging on truncate

有一个可选的 IDENTIFIER 子句,用于标记特定会话中的错误。假定您执行以下命令:
SQL> set errorlogging on identifier MYSESSION1

现在,如果运行脚本,将使用名为 IDENTIFIER 的列创建记录,并在列中填充值 MYSESSION1。通过执行以下查询可以提取这些记录:
select timestamp, username, script, statement, message
from sperrorlog
where identifier = 'MYSESSION1';

您将只看到该会话中的记录。如果要将多个脚本和会话中的错误分开,此功能非常有用。

 

缩小临时表空间

您可能已经知道,临时表空间很特殊,普通的空间管理规则可能并不适用。分配了临时段之后,将不会再回收它。这不是一个真正的问题,因为临时段(使用临时表空间)不是模式的一部分,也不会在数据库回收空间中存储。其他用户或查询可重用此空间。不管怎样,由于此空间不再回收,因此临时表空间只能不断增大。但是,我们能否对这些空间进行修剪,从而为其他表空间腾出一些空间呢?

到目前为止,唯一的办法就是删除并重新创建表空间 — 这只是小事一桩,几乎可以在保持联机的状态下完成。不过也存在一点“小问题”:如果要求正常运行时间必须达到百分之百时怎么办?在 Oracle Database 11g 中,通过缩小临时表空间可以很容易实现这一点。

下面介绍如何缩减表空间 TEMP1:

alter tablespace temp1 shrink space;

此命令回收表空间中所有的未用段,从而缩小表空间。完成上述操作后,可通过 DBA_TEMP_FREE_SPACE 视图查看当前已分配的空间和可用空间是多少。
SQL> select * from dba_temp_free_space;
 
TABLESPACE_NAME TABLESPACE_SIZE ALLOCATED_SPACE FREE_SPACE
------------------------------ ---------------        ---------------        ----------
TEMP            179306496       179306496      178257920

在相对静止的数据库中,缩小操作几乎可以将临时表空间缩小为空。这当然不会成为真实情况;由于后续活动将扩大表空间,因此您可能希望在内部留出一些空间,假定为 100MB。可以执行以下命令:

SQL> alter tablespace temp shrink space keep 100m;
 
Tablespace altered.
 
SQL> select * from dba_temp_free_space;
 
TABLESPACE_NAME TABLESPACE_SIZE ALLOCATED_SPACE FREE_SPACE
------------------------------ ---------------         ---------------       ----------
TEMP            105906176        1048576        104857600

仅留下 100MB,其余空间都被释放。此方法有助于在各种表空间中管理空间。现在,您可以从某个临时表空间中借用一些空间,临时提供给其他表空间(并非故意使用双关语)。如果日后不再需要此空间,可将其还给临时表空间。将此特性与用于全局临时表的表空间结合起来使用时,可以解决临时表空间中的许多空间管理难题。

 

SQL*Plus 显示 BFILE

Oracle 数据库中的 BFILE 数据类型存储指向外部文件的指针,而不存储文件本身的内容。它具有以下特定用途:当用户可以访问文件中的数据时可以节省数据库空间,这对于某些应用程序(如使用内容和多媒体丰富的数据库的应用程序)来说非常有用。

如果表中有一个 BFILE 类型列,您要在 SQL*Plus 中选择表的某些行,这在过去常常会发生错误。在 Oracle Database 11g 中,SQL*Plus 将文件位置作为列值显示。

让我们来看一个示例。首先,必须创建一个用于存储 BFILE 的目录。

SQL> create directory doc_dir as '/u01/oracle/docs';

Directory created.

现在创建表。
 SQL> create table docs
   2  (
   3     doc_id          number(20),
   4     doc_file        bfile,
   5     doc_type        varchar2(10)
   6  );

Table created.

创建行。
SQL> insert into docs values
  2  (
  3     1,
  4     bfilename('DOC_DIR','metric_daily_report.pdf'),
  5     'PDF'
  6  );
 
1 row created.

现在,如果在 SQL*Plus 中选择此行:
SQL> col doc_file format a50
SQL> select * from docs;
 
DOC_ID DOC_FILE                                          DOC_TYPE
----------  --------------------------------------------------                         ----------
  1    bfilename('DOC_DIR', 'metric_daily_report.pdf')    PDF

DOC_FILE 列下的输出(为 BFILE 类型)显示文件位置,而不显示错误消息。

 

内存中的参数文件

考虑如下情形:在诊断某个数据库问题期间,您修改了很多参数,这些参数都位于内存中。后来,您忘记更改了哪些参数。除非您记得将这些参数放入初始化参数文件(pfile 或 spfile)中,否则这些更改将会丢失。您很有可能未将这些参数放入该文件,因为您正在测试这些值,尚未确定要使用哪个特定值。当然,您可以查看警报日志并找出所作的全部更改,但这一过程既费力又容易出错。

在 Oracle Database 11g 中,可以使用一个非常简单的命令,通过内存中的参数值来创建 pfile 或 spfile。

SQL> create spfile from memory;

File created.
 
SQL> create pfile from memory;
 
File created.

这将使用内存中的值创建 spfile 或 pfile。pfile 文件的顶部将包含如下条目:
# Oracle init.ora parameter file generated by instance ODEL11 on 08/28/2007 14:52:14

这将捕获内存中已更改的参数,可以为您节省大量精力,并会大大降低风险。此特性还允许您使用其他名称创建 pfile,然后将当前 pfile 与所生成的文件进行比较,以查看更改了哪些参数。
SQL> create pfile='/tmp/a' from memory;

此语句将创建一个名为 /tmp/a 的 pfile。在 Unix 中,可以使用简单的 diff 命令显示这两个文件之间的差异。

 

谨慎重置

在性能调优过程中,您要检查直接在 spfile 上更改几个动态初始化参数所造成的影响,突然您担心之后会找不到所作的更改。由于这些内容是在 spfile 中更改的,因此您不能指望 spfile 告诉您更改了哪些值。

没关系,可以使用以下方法:ALTER SYSTEM RESET 命令将值重置为默认值,覆盖您在 spfile 中所作的更改:

alter system reset license_max_sessions;

早期版本中也提供了此命令,但存在一个根本区别:在早期版本中,它需要在 RAC 数据库中使用 SID 子句。如果您要在所有实例上重置,应指定 SID='*' 子句。在 Oracle Database 11 g 中,SID 子句是可选的,默认值为所有实例。因此,在以前版本中如果忽略该子句将会出错;现在则不会出现这种情况,但效果可能并不如您所愿。因此,重置时要谨慎。

 

实例 CPU 囚笼(仅限第 2 版)

很多组织由于各种原因选择在一台服务器上运行多个数据库实例。无论何种原因,这通常都不是一个好主意。最佳替代方法是将所有这些应用程序整合到该服务器上运行的一个数据库中。这样做的结果是只存在一组 Oracle 后台进程并且只存在一组通用表空间(system、sysaux、undo、temp 等)。

相反,多个实例会各自要求自己的一组进程和存储,这会对硬件造成压力。更糟的是,一个实例可能完全独霸硬件的资源,如 CPU、I/O 通道和内存。可通过设置 SGA 大小来为某个实例保留实例内存,但是无法保留 PGA 内存。无法为特定实例保留 CPU 和 I/O(通过专门的 O/S 调节工具可能会有一些例外)。

在 Oracle Database 11g 第 2 版中,可以通过一个被称作实例囚笼的新特性在各实例间实现受限的 CPU 分配。(请注意,该限制适用于用户进程,但不适用于 pmon、smon、arch 等后台进程)这种分配通过数据库资源管理器和系统参数 cpu_count 来实现。默认情况下,实例通过查询操作系统中的 CPU 数量来设置 CPU_COUNT 参数。使用资源管理器,您可以将此参数更改为更小的值,从而将实例限制为使用指定数量的 CPU。

我们来看一下它的工作原理。首先,准备一个资源管理器计划。首先进行检查;您可能已经启用了一个资源管理器计划。该计划必须具有一个 CPU 指令,其他内容都不是必需的。

begin
        dbms_resource_manager.clear_pending_area();
        dbms_resource_manager.create_pending_area();
        dbms_resource_manager.create_plan (
                plan     => 'lim_1_cpu',
                comment  => 'Plan to limit CPUs to just 1',
                max_iops => null,
                max_mbps => null
        );
        dbms_resource_manager.create_plan_directive(
                plan             => 'lim_1_cpu',
                group_or_subplan => 'OTHER_GROUPS',
                comment          => 'Other Groups',
                mgmt_p1          => null,
                mgmt_p2          => 100
        );
        dbms_resource_manager.create_plan_directive(
                plan             => 'lim_1_cpu',
                group_or_subplan => 'SYS_GROUP',
                comment          => 'System Groups',
                mgmt_p1          => 50,
                mgmt_p2          => null
        );
        dbms_resource_manager.submit_pending_area();
end;
/

然后激活该计划:

SQL> alter system set resource_manager_plan = 'LIM_1_CPU';

System altered.


然后将 CPU 计数设为 1。

SQL> alter system set cpu_count = 1;


该指令现在将用户进程限制为一个 CPU(仅对所有用户进程)。切记,后台进程无论如何都将消耗所有 CPU。

调度程序电子邮件通知(仅限第 2 版)

相对于以前的 DBMS_JOB 功能,调度程序长期以来一直提供各种优势。现在您又多了一个喜欢它的理由。

当一个作业完成时,您如何得知?反复查询 DBA_SCHEDULER_JOBS 视图将会告诉您结果,但这几乎不具备实用性。更实用的解决方案是使用电子邮件。但是这带来了自身的一系列问题 — 从变更控制审批到无法修改源代码。

在 Oracle Database 11g 第 2 版中,您无需采用这些方法;有了更加好用的替代方法,即,调度程序会在作业完成后发送电子邮件。甚至还可在电子邮件中指出作业的完成是以成功还是失败结束。

为了进行演示,我们来创建一个运行名为 process_etl2 的存储过程的作业:

begin
  dbms_scheduler.create_job (
    job_name        => 'process_etl2',
    job_type        => 'STORED_PROCEDURE',
    job_action      => 'process_etl2',
    start_date      => SYSTIMESTAMP,
    repeat_interval => 'freq=minutely; bysecond=0',
    enabled         => TRUE);
end;
/

现在,为了启用电子邮件发送功能,我们必须设置一些与电子邮件相关的参数,如电子邮件服务器名称以及应以何种方式指定发件人的详细信息。

BEGIN  
  dbms_scheduler.set_scheduler_attribute(
    'email_server', 
    'mail.proligence.com:25'
  );  
  dbms_scheduler.set_scheduler_attribute(
     'email_sender', 
     'dbmonitor@proligence.com'
  );
END;
/

请注意,SMTP 服务器以 server[:port] 格式提供。如果未提供端口,将采用默认值 25。现在,我们可以向作业中添加电子邮件通知属性:

begin
 dbms_scheduler.add_job_email_notification (
  job_name   =>  'process_etl2',
  recipients =>  'arup@proligence.com',
  subject    =>  'Job: process_etl',
  body       =>  'ETL Job Status',
  events     =>  'job_started, job_succeeded');
END;
/

该过程的参数具有自我说明性。一个非常重要的参数是 EVENTS,它指定在哪些事件发生期间应发送电子邮件。在该示例中,我们已指定在作业开始及作业成功时发送电子邮件(作业失败时不发送)。

EVENTS 参数可具有表值 job_failed、job_broken、job_sch_lim_reached、job_chain_stalled 和 job_over_max_dur,分别表示作业最终失败、作业损坏、作业已达到调度程序中的限制,作业所属的链已处于停滞状态以及作业超出了其持续时间。

如果要了解位于此通知系统下的各种作业所发送的电子邮件通知的状态,可以检查新数据字典视图 USER_SCHEDULER_NOTIFICATIONS。

SQL> desc user_scheduler_notifications
 Name                                      Null?    Type
 -----------------------------------------                     --------     ----------------------------
 JOB_NAME                                  NOT NULL VARCHAR2(30)
 JOB_SUBNAME                                        VARCHAR2(30)
 RECIPIENT                                 NOT NULL VARCHAR2(4000)
 SENDER                                             VARCHAR2(4000)
 SUBJECT                                            VARCHAR2(4000)
 BODY                                               VARCHAR2(4000)
 FILTER_CONDITION                                   VARCHAR2(4000)
 EVENT                                              VARCHAR2(19)
 EVENT_FLAG                                NOT NULL NUMBER

我们来看一下该视图的内容。

SQL> select job_name, recipient, event
  2  from user_scheduler_notifications;

JOB_NAME                  RECIPIENT            EVENT
-------------------------             --------------------           -------------------
PROCESS_ETL2              arup@proligence.com  JOB_STARTED
PROCESS_ETL2              arup@proligence.com  JOB_SUCCEEDED


body 列显示所发送的实际邮件:

SQL> select BODY, event_flag
  2  from user_scheduler_notifications
  3  /

BODY
--------------------------------------------------------------------------------
EVENT_FLAG
----------
ETL Job Status
         1

ETL Job Status
         2


根据所定义的错误,电子邮件将持续不断。您还可以定义一个具有如下功能的筛选器:仅当错误代码为 ORA-600 或 ORA-7445 时才通知系统发送电子邮件。该表达式应该是一个有效的 SQL 谓词(没带“where”关键字的 WHERE 子句)。

BEGIN
 DBMS_SCHEDULER.add_job_email_notification (
  job_name   =>  'process_etl2',
  recipients =>  'arup@proligence.com',
  subject    =>  'Job: process_etl',
  body       =>  'ETL Job Status',
  filter_condition =>  ':event.error_code = 600 or :event.error_code = 7445',
  events     =>  'job_started, job_succeeded');
END;
/


您是否注意到 body 只是一个简单的“ETL Job Status”?这个意义不大。用默认值代替它会更好。默认情况下,将发送具有以下正文的电子邮件通知

Job: %job_owner%.%job_name%.%job_subname%
Event: %event_type%
Date: %event_timestamp%
Log id: %log_id%
Job class: %job_class_name%
Run count: %run_count%
Failure count: %failure_count%
Retry count: %retry_count%
Error code: %error_code%
Error message: %error_message%' 


正如您所看到的,正文具有许多变量,如 %job_owner%。下表对这些变量进行了解释:

 

变量

描述

%job_owner% 

作业的所有者

%job_name% 

作业的名称

%job_subname% 

当作业是一个基于事件的作业时,该列显示事件链

%event_type% 

触发通知的事件,例如 job_broken、job_started 等

%event_timestamp% 

事件的发生时间

%log_id% 

作业执行的详细信息位于视图 DBA_SCHEDULER_JOB_LOG 和 DBA_SCHEDULER_JOB_RUN_DETAILS 中。这些视图中的 LOG_ID 列与该列相对应。

%error_code% 

错误代码(如果有的话)

%error_message% 

错误消息(如果有的话)

%run_count% 

该作业运行的次数

%failure_count% 

该作业失败的次数

%retry_count% 

该作业失败后重试的次数


要删除通知,可以使用同一软件包中的另一个过程:

begin
 dbms_scheduler.remove_job_email_notification (
  job_name   =>  'process_etl2');
end;
/


电子邮件通知使作业系统完整无缺。切记,电子邮件是由调度程序作业系统触发的,而不是由调度程序调用的程序或过程中的代码触发。这使您可以独立于作业下的实际代码来设置通知调度。

SQL*Plus 隐式提交(仅限第 2 版)

SQL*Plus 这个古老的工具除了在 Oracle Database 11g 第 2 版中有一个改动之外,近来并没有多大变化。新版本中,该工具的行为有了一个非常微小但具有潜在影响的更改。到目前为止,如果您执行一个事务但不发出显式提交或回滚,那么 SQL*Plus 会在事务结束时发出显式提交或回滚。如果是正常结束(即,您发出了退出或断开连接),那么会执行提交,否则将回滚事务。

在某些情况下,可能不需要这一默认行为。如果用户未发出显式提交,那么您可能希望在退出的情况下回滚事务。在第 2 版中,可以通过一个名为 exitcommit 的新参数来更改这一行为。我们来看一下它在两种不同 UNIX 提示符下的工作方式。

终端 1

终端 2

连接到 SQL*Plus
 # sqlplus arup/arup      
SQL> create table test (col1number);      
Table created.      
SQL> insert into test values (1);     
1 row created.   
SQL> commit;  
Commit complete
           
SQL> update test set col1 =	
           
1 row updated.
 
           
Note: a commit has not been issued yet.

 

 

此时,检查来自这个不同会话的值。它应仍显示提交前的值。

 

SQL> select col1 from test; 
      
 COL1
----------
 1      
  
1 row selected.

 

Exit from SQL*Plus normally:
SQL> exit           
Disconnected from Oracle 				
Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
With the Partitioning, Automatic Storage Management, 
OLAP, Data Mining and Real Application Testing 			
	options

 

 

 

检查该列的值:

 

SQL> select col1 from test;
         
 COL1
----------
 2
         
1 row selected.

 

值现在为 2。为什么?因为 exit 语句自动发出了 commit 语句。

现在,将 exitcommit 参数设置为 OFF 并重复以上实验:

 SQL> set exitcommit off
SQL> update test set col1 = 	3;        
1 row updated.     
SQL> exit

注:在 exit 语句之前未发出 commit 语句。

 

 

 

检查该会话中的值:

 

SQL> select col1 from test;       
 
COL1
----------
 2

 

值仍为 2,也就是先前的值。由于设置了 exitcommit 参数,因此其他会话未发出 commit 语句。

 


但是,该参数仅影响 EXIT 语句。如果您仍在 SQL*Plus 中,但是只是通过发出 DISCONNECT 结束了会话,那么该参数没有任何效果。在这种情况下,仍将是提交的默认行为。在会话 1 中,将值更新为 4 之后,发出 disconnect 而非 exit。

SQL> disconnect

On session 2, check the value

SQL> select col1 from test;

      COL1
      ----------
         4
         

由于 DISCONNECT 命令不受 EXITCMMIT 设置的影响,因此提交了操作。如果您在 SQL*Plus 中,但是希望连接到其他用户,您将发出:

SQL> connect differentuser/<password>


该操作发出隐式 DISCONNECT;因此将提交所有事务。您可能需要记住以下事实:exitcommit 仅影响 EXIT 语句;不影响 DISCONNECTS - 无论显式还是隐式。

 

转载至:

http://www.oracle.com/technetwork/cn/articles/sql/11g-misc-094345-zhs.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值