当GLOBAL_NAMES参数设置为TRUE时,使用DATABASE LINK时,DATABASE LINK的名称必须与被连接库的GLOBAL_NAME一致。下面做一个测试,在测试中,创建数据库链接的库为XJ(WINDOWS 2003 ORACLE 10g 10.2.0.1),被链接的库为DMDB(LINUX AS5 ORACLE 10g 10.2.0.1 RAC)

首先查看DMDB的相关配置:

SQL> show parameter global_names

NAME                                 TYPE        VALUE
------------------------------------ ----------- ---------
global_names                         boolean     FALSE
SQL> select * from global_name;;

GLOBAL_NAME
----------------------------------------------------------
DMDB

可以看到这个被链接的库其global_names参数为FALSE。

要创建数据库链接的库的配置:

SQL> show parameter global_names

NAME                                 TYPE        VALUE
------------------------------------ ----------- -------
global_names                         boolean     FALSE
SQL> select * from global_name;

GLOBAL_NAME
--------------------------------------------------------
XJ

然后做下面的操作:

SQL> create database link test_link connect to test identified by test using 'DMDB';

数据库链接已创建。

SQL> select * from dual@test_link;

D
-
X

可以看到数据库链接工作正常。

在DMDB库上将global_names设为TRUE:

SQL> alter system set global_names=true;

系统已更改。

在XJ库上再次查询,并新建一个DATABASE LINK再进行查询:

SQL> select * from dual@test_link;

D
-
X

SQL> create database link test_link2 connect to test identified by test using 'D
MDB';

数据库链接已创建。

SQL> select * from dual@test_link2;

D
-
X

此时可以看数据库链接工作正常。我们再将XJ库的global_names参数设置为TRUE:

SQL> alter system set global_names=true;

系统已更改。

SQL> select * from dual@test_link2;
select * from dual@test_link2
                   *
第 1 行出现错误:
ORA-02085: 数据库链接 TEST_LINK2 连接到 DMDB

SQL> select * from dual@test_link
select * from dual@test_link
                   *
第 1 行出现错误:
ORA-02085: 数据库链接 TEST_LINK 连接到 DMDB

而再次将XJ库的global_names设为FALSE,则数据库链接又可用了。
SQL> alter system set global_names=false;

系统已更改。

SQL> select * from dual@test_link;

D
-
X

SQL> select * from dual@test_link2;

D
-
X

再将DMDB库的global_names设为FALSE,数据库链接仍然可用:

在DMDB库上:

SQL> alter system set global_names=false;

系统已更改。

在XJ库上:

SQL> select * from dual@test_link;

D
-
X

可以看到,链接仍然可以用。
如果在DMDB库上创建链接到XJ库上,可以观察到同样的结果。
可以得出一个结论:global_names参数设置为TRUE(感谢Robert.Li指出的此前的错误),影响的是创建数据库链接的那个库对数据库链接的使用。也就是说,如果一个库(实例)的global_names参数设值为TRUE,则该库连接其他库的数据库链接,其名称必须要与被连接的库的global_name相同:

在XJ库上:

SQL> alter system set global_names=true;

系统已更改。

SQL> create database link dmdb connect to test identified by test using 'dmdb';

数据库链接已创建。

SQL> select * from dual@dmdb;

D
-
X
SQL> select * from dual@test_link;
select * from dual@test_link
*
第 1 行出现错误:
ORA-02085: 数据库链接 TEST_LINK 连接到 DMDB

SQL> select * from dual@test_link2;
select * from dual@test_link2
                   *
第 1 行出现错误:
ORA-02085: 数据库链接 TEST_LINK2 连接到 DMDB

如果在GLOBAL_NAMES设置为TRUE的情况下,如果要建多个数据库链接到同一个库,怎么办呢?因为数据库链接的名称必须与目标库的GLOBAL_NAME相同。可以按如下的方法:

SQL> create database linkdmdb@link1connect to test identified by test using 'dmdb';

数据库链接已创建。

SQL> create database linkdmdb@link2connect to test identified by test using 'dmdb';

数据库链接已创建。

SQL> select * fromdual@dmdb;

D
-
X

SQL> select * fromdual@dmdb@link1;

D
-
X

SQL> select * fromdual@dmdb@link2;

D
-
X

也就是在GLOBAL_NAME后面加上@再加上一个标识。这样就能够创建多个数据库链接到同一目标库上了。

另外在创建数据库链接时,不能给其他SCHEMA创建链接,这是因为数据库链接(database link)其名称可以含有'.'即点号。比如A用户想给B用户创建一个DBLINK名叫LINKB,CREATE DATABASE LINK B.LINKB ......, 这个语句将会实际创建一个A用户下的名为B.LINKB的数据库链接。

SET TRANSACTION READ ONLY类似于SERIALIZABLE事务隔离级别,在发布SET TRANSACTION READ ONLY起的所有SELECT语句,其结果均为同一个时间点一致,直至显式地发布了COMMIT或ROLLBACK命令或隐式提交(执行DDL)。这个时间点为SET TRANSACTION READ ONLY这个语句执行后的时间点。这个语句与SERIALIZABLE不同之处在于,在READ ONLY这个范围内,不能进行DML。以下用测试说明:

用TEST1用户开启两个会话

在会话一中:

SQL> create table t1 (a int );

Table created.

SQL> insert into t1 values (10);

1 row created.

SQL> commit;

Commit complete.

在会话二中:

SQL> select * from t1;

         A
----------
        10

SQL> set transaction read only;

Transaction set.

SQL> select * from t1;

         A
----------
        10
然后在会话一中插入一行数据,并提交:

SQL> insert into t1 values (20);

1 row created.

SQL> commit;

Commit complete.

在会话二中查看表t2的数据:

SQL> /

         A
----------
        10

SQL> /

         A
----------
        10

SQL> commit;

Commit complete.

SQL> select * from t1;

         A
----------
        10
        20

可以看到,虽然会话一已经插入了一条数据并提交了,但是查询时,仍然只能看到一条数据。在COMMIT之后,SET TRANSACTION READ ONLY作用结束,再查询T1,可以看到新插入的数据了。

我们再看一下,这个“时间点”是从第一个SELECT语句的时候还是SET TRANSACTION READ ONLY刚执行完的时候:

在会话二中:

SQL> set transaction read only;

Transaction set.

然后在会话一中:

SQL> insert into t1 values (30);

1 row created.

SQL> commit;

Commit complete.

在会话二中:

SQL> select * from t1;

         A
----------
        10
        20

可以看到,新插入的数据30是在会话二的SET TRANSACTION READ ONLY之后和SELECT之前插入的,但SELECT语句看不到这个数据,因此这个时间点是在执行完SET TRANSACTION READ ONLY之后,而不是第一个SELECT语句执行那一刻。

我们继续下面的测试:

在会话二中:

SQL> drop table t2;

Table dropped.

SQL> select * from t1;

         A
----------
        10
        20
        30

可以看到DROP语句之后,由于隐式提交,SET TRANSACTION READ ONLY作用范围结束,又可以查到新插入的数据。

SQL> set transaction read only;

Transaction set.

SQL> insert into t1 values (40);
insert into t1 values (40)
            *
ERROR at line 1:
ORA-01456: may not perform. insert/delete/update operation inside a READ ONLY
transaction
可以看到,在SET TRANSACTION READ ONLY之后,不能执行DML

注意:SYS用户并不受SET TRANSACTION READ ONLY的影响:

SQL> show user
USER is "SYS"
SQL> set transaction read only;

Transaction set.

SQL> delete from t1 where rownum=1;

1 row deleted.

SQL> commit;

Commit complete.

以上测试即证明了这一点。

EXP导出数据时,如果CONSISTEN参数设为TRUE,则EXP导出时,会先发布SET TRANSACTION READ ONLY,保证所有导出数据在同一时间点上的一致性。当然,如果事务频繁,导出的数据量又大,很可能会遭遇ORA-01555错误。由于SET TRANSACTION READ ONLY对SYS用户无效,用SYS用户导出时CONSISTENT设为TRUE,应该没有效果。有兴趣的朋友可以进行测试。

Primary key 与Unique Key都是唯一性约束。但二者有很大的区别:

1、Primary key的1个或多个列必须为NOT NULL,如果列为NULL,在增加PRIMARY KEY时,列自动更改为NOT NULL。而UNIQUE KEY 对列没有此要求。

2、一个表只能有一个PRIMARY KEY,但可以有多个UNIQUE KEY。

下面以测试说明:

SQL> create table t (a int,b int,c int,d int);

Table created.

SQL> desc t
 Name                                      Null?    Type
 ----------------------------------------- -------- -----------

 A                                                  NUMBER(38)
 B                                                  NUMBER(38)
 C                                                  NUMBER(38)
 D                                                  NUMBER(38)

SQL> alter table t add constraint pk_t primary key (a,b);

Table altered.

SQL> desc t
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------

 A                                         NOT NULL NUMBER(38)
 B                                         NOT NULL NUMBER(38)
 C                                                  NUMBER(38)
 D                                                  NUMBER(38)

可以看到A、B两个列都自动改为了NOT NULL

SQL> alter table t modify (a int null);
alter table t modify (a int null)
                      *
ERROR at line 1:
ORA-01451: column to be modified to NULL cannot be modified to NULL
可以看到,列A不允许改为NULL

SQL> alter table t drop constraint pk_t;

Table altered.

SQL> alter table t add constraint uk_t_1 unique (a,b);

Table altered.

SQL> desc t
 Name                                      Null?    Type
 ----------------------------------------- -------- -----------

 A                                                  NUMBER(38)
 B                                                  NUMBER(38)
 C                                                  NUMBER(38)
 D                                                  NUMBER(38)

我们看到列A又变回了NULL。

注意到,在删除主键时,列的NULLABLE会回到原来的状态。如果在创建主键后,对原来为NULL的主键列,显式设为NOT NULL,在删除主键后仍然是NOT NULL。比如在创建主键后,执行下面的操作,可以看到:

SQL> alter table t modify (b int not null);

Table altered.

SQL> alter table t drop constraint pk_t;

Table altered.

SQL> desc t
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------

 A                                                  NUMBER(38)
 B                                         NOT NULL NUMBER(38)
 C                                                  NUMBER(38)
 D                                                  NUMBER(38)

再做如下的实验:

SQL> drop table t;

Table dropped.

SQL> create table t (a int,b int,c int,d int);

Table created.

SQL> alter table t add constraint uk_t_1 unique (a,b);

Table altered.

SQL> alter table t add constraint uk_t_2 unique (c,d);

Table altered.

可以看到可以增加两个UNIQUE KEY。看看能不能增加两个主键:

SQL> alter table t add constraint pk_t primary key (c);

Table altered.

SQL> alter table t add constraint pk1_t primary key (d);
alter table t add constraint pk1_t primary key (d)
                                  *
ERROR at line 1:
ORA-02260: table can have only one primary key
由此可以看到一个表只能有一个主键。

SQL> alter table t drop constraint pk_t;

Table altered.

SQL> insert into t (a ,b ) values (null,null);

1 row created.

SQL> /

1 row created.

SQL> insert into t (a ,b ) values (null,1);

1 row created.

SQL> /
insert into t (a ,b ) values (null,1)
*
ERROR at line 1:
ORA-00001: unique constraint (SYS.UK_T_1) violated

SQL> insert into t (a ,b ) values (1,null);

1 row created.

SQL> /
insert into t (a ,b ) values (1,null)
*
ERROR at line 1:
ORA-00001: unique constraint (SYS.UK_T_1) violated

主键和唯一键约束是通过参考索引实施的,如果插入的值均为NULL,则根据索引的原理,全NULL值不被记录在索引上,所以插入全NULL值时,可以有重复的,而其他的则不能插入重复值。

个人认为ORACLE的学习,就好比武侠小说中学武功。要从三方面入手:

1、内功:针以学习ORACLE来说,内功就是对基本概念的掌握,ORACLE架构的深入理解,原理的掌握。如果有兴趣和时间,研究一下Internal的东西,这好比修习易筋经,需要极大的毅力和长期的坚持。

2、招式:如果光有内功,没有招式,则会陷入空有高深内力,却无从发招的尴尬。学习ORACLE也一样,还是需要掌握功能的使用,具体到SQL的使用,各个性能视图的使用,数据字典的使用。如果没有这些,在进行操作时,会有找不到无从下手的感觉。

3、实战经验:武侠世界中的高手,都是从无数次战斗中取得经验,再武功大进。学习ORACLE也一样,如果没有充分的实验,实际生产环境的实战,仍然只能说是只能入了ORACLE的门,算不上登入大堂。

另外,学习过程中,多做笔记,多思考。做任何事,都需要多思考,学习ORACLE也不例外。对ORACLE的众多的功能和知识点,我们要经常思考,这个功能有什么好处,适用于什么地方,不适用于什么地方,每个知识点之间的联系等等。甚至是要站在超越ORACLE的高度,去思考ORACLE为什么会这样设计。另外,好记性不如烂笔头,除非是天才,很多东西,久了就忘记了。比如前段时间做过CLUSTER表的实验,现在都快忘记了,现赶紧记之(^_^):

对CLUSTER下的表,不能TRUNCATE,只能TRUNCATE CLUSTR,这样TRUNCATE CLUSTR时,将截断CLUSTER下的所有表。

DROP CLUSTER时,如果CLUSTER下有表,则不能DROP。只能将CLUSTER中所有的表DROP后,才能DROP CLUSTER。

关于ORACLE 的transactional方式下的shutdown ,做了一下实验,记录之:

正常启动ORACLE之后,开三个连接到ORACLE。

在session2执行
SQL> delete from t1 where rownum=1;

1 row deleted.

在session 3执行
SQL> delete from t2 where rownum=1;

1 row deleted.

在session1执行
SQL> shutdown transactional;

这个session会挂起。

我们在session2执行如下的语句:
SQL> rollback;

Rollback complete.

SQL> delete from t1 where rownum=1;
delete from t1 where rownum=1
            *
ERROR at line 1:
ORA-01089: immediate shutdown in progress - no operations are permitted
SQL> select 1 from dual;

         1
----------
         1

可以看到,事务回滚后,不能开启新的事务了。但是可以进行查询操作

现在我们在session3中回滚事务:
SQL> rollback;

回退已完成。

这个时候在session 1中就会出现:
数据库已经关闭。
已经卸载数据库。
ORACLE 例程已经关闭。
SQL>

结论:以transactional方式关闭数据库,当客户端的事务完成后(commit 或 rollback),客户端就不能再次开始一个新的事务,但是可以进行查询。当所有客户端的事务完成后,服务器就会强制断开所有连接,然后关闭数据库。

另外,在以normal方式关闭数据库时,已有的连接不受任何影响,可以新开始一个事务。只是不允许连接新的用户。只有所有的用户都disconnect后,实例才能关闭。对于一个实际的业务系统来说,几乎是一个“不可能完成的任务”。

大家都知道从Oracle8开始,Oracle开始使用“相对文件号”,使原来一个数据库最多只能有1023个文件,扩展为一个表空间最多可以有1023个文件,每个库最多可以有65534个文件。

我们来作一个测试:

SQL> create tablespace test_mf datafile 'F:\Works\oracle\product\10.2.0\oradata\
xj\many\m1.dbf' size 100k reuse;

表空间已创建。

SQL> alter tablespace test_mf add datafile 'F:\Works\oracle\product\10.2.0\orada
ta\xj\many\m2.dbf' size 88k;

表空间已更改。

SQL> show parameter db_files

NAME                        TYPE        VALUE
--------------------------- ----------- ---------------
db_files                    integer     2000

SQL> begin
  2    for i in 193..1025 loop
  3       execute immediate'alter tablespace test_mf add datafile ''F:\Works\ora
cle\product\10.2.0\oradata\xj\many\m_' || i ||''' size 88k';
  4   end loop;
  5  end;
  6  /
begin
*
第 1 行出现错误:
ORA-01686: 最大文件数 (1023) 对于表空间 TEST_MF 已达到
ORA-06512: 在 line 3

SQL> select count(*) from dba_data_files where tablespace_name='TEST_MF';

  COUNT(*)
----------
      1023

可以看到表空间TEST_MF的文件数为1023个,最多也只能为1023个。

SQL> select ts# from v$tablespace where name='TEST_MF';

       TS#
----------
         8

SQL> select file#,rfile#,name from v$datafile where ts#=8;

     FILE#     RFILE# NAME                                                                                             
---------- ---------- ------------------------------------------------------------                                     
         7          7 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\M1.DBF                                            
         8          8 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\M2.DBF                                            
         9          9 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\M_1                                               
        10         10 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\M_2                                               
        11         11 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\M_3                                               
..................................................................................
      1019       1019 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\M_1011                                            
      1020       1020 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\M_1012                                            
      1021       1021 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\M_1013                                            
      1022       1022 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\M_1014                                            
      1023       1023 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\M_1015                                            
      1024          1 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\M_1016                                            
      1025          2 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\M_1017                                            
      1026          3 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\M_1018                                            
      1027          4 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\M_1019                                            
      1028          5 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\M_1020                                            
      1029          6 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\M_1021
                                            

从上面的数据可以看出,当绝对文件号小于等于1023,相对文件号与绝对文件号一样。相对文件号大于1023之后,又从1开始循环。

我们DUMP最后一个文件的文件头块看看:

Block Header:
block type=0x0b (file header)
block format=0xa2 (oracle 10)
block rdba=0x01800001 (file#=6, block#=1)
scn=0x0000.00000000, seq=1, tail=0x00000b01
block checksum value=0xe7f3=59379, flag=4
File Header:
Db Id=0xb004e979=2953111929, Db Name=XJ, Root Dba=0x0
Software vsn=0x0, Compatibility Vsn=0xa200100, File Size=0xb=11 Blocks
File Type=0x3 (data file), File Number=1029, Block Size=8192
Tablespace #8 - TEST_MF rel_fn:6

文件头里面有两部分内容,第一部分为块头,块头记录了该块的RDBA:block rdba=0x01800001 (file#=6, block#=1),因此块头记录的是相对文件号。第二部分为文件头,文件头里面有如下的记录:
    File Type=0x3 (data file), File Number=1029, Block Size=8192
   Tablespace #8 - TEST_MF rel_fn:6
因此文件头里同时记录了文件绝对号,表空间号和相对文件号。

下面我们再做另一个实验,看看段是怎么跟文件号关联的。

SQL> create tablespace test_lf datafile 'F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\
XJ\MANY\TEST_LF.dbf' size 1m;

表空间已创建。

SQL> select ts# from v$tablespace where name='TEST_LF';

       TS#
----------
         9

SQL> select file#,rfile#,name from v$datafile where ts#=9;

     FILE#     RFILE# NAME
---------- ---------- ------------------------------------------------------------
      1030          7 F:\WORKS\ORACLE\PRODUCT\10.2.0\ORADATA\XJ\MANY\TEST_LF.DBF

从上面的数据可以看出,一个表空间的数据文件,其相对文件号并不是从1开始的,而依然是从上一个用过的最后一个相对文件号继续。

SQL> select obj# from obj$ where owner#=0 and name='T1';

      OBJ#
----------
     47686

SQL> select obj#,dataobj#,ts#,file# from tab$ where obj#=47686;

      OBJ#   DATAOBJ#        TS#      FILE#
---------- ---------- ---------- ----------
     47686      47686          9          7

在数据字典里面记录了表的段头表空间号和相对文件号。

SQL> select header_file,header_block,relative_fno from dba_segments where segmen
t_name='T1' and wner='SYS';

HEADER_FILE HEADER_BLOCK RELATIVE_FNO
----------- ------------ ------------
       1030           11            7

在DBA_SEGMENTS视图里面,可以查到段头的相对文件号和绝对文件号。(这个视图最终是从file$、seg$等字典表里面取得数据)

有网友在ITPUB上问到“sqlplus下如何获取shell的return code”

可以用以下的办法:

在SHELL中把结果重定向到一个文件。
举个例子:
所有的测试文件都放在$HOME下
func.sh是你要执行操作的SHELL

#!/bin/sh
exit 2

test.sh的功能就是调用func.sh
#!/bin/sh
$HOME/func.sh
echo "define VAR_TEST=$?" > $HOME/test.sql

在SQLPLUS中
SQL > host $HOME/test.sh
SQL > @$HOME/test.sql

现在就定义了一个SQLPLUS的变量VAR_TEST。
SQL > select '&VAR_TEST' x from dual;

old    1: select '&VAR_TEST' x from dual
new  1:select '2' x from dual

x
--
2

,

一客户重启主机,启动数据库之后,客户端连接报ORA-12541:没有监听器的错误

环境:ORACLE 10.2.0.1 RAC AIX 5.3

进行测试,发现结点1不正常。客户端通过PUBLIC IP可以连接,但通过VIP不能进行连接,报ORA-12541错误

检查结点1的VIP,正常。在客户端PING VIP,正常。TNSPING VIP,不通。

检查监听配置,正常。

重启监听,故障依旧。

在客户端(WINDOWS系统),用arp -a命令检查发现,结点1的PUBLIC IP和VIP的MAC地址不一致。怀疑是其他机器占用了此IP。

在客户端用nbtstat -A VIP地址,发现是一WINDOWS系统机器占用了此IP,并得到机器名。

客户维护人员解决IP地址冲突,故障排除。

故障解决很快,但此故障引发的问题值得紧记:在管理数据库的同时,别忘网络的管理。特记之。

大家都知道,ORACLE实例在启动时,或使用命令ALTER SYSTEM REGISTER 或每隔一分钟,实例的PMON会向监听进行注册,告知监听,实例的服务名,实例名等信息。

不同的平台有不同的行为,本文所描述的是在LINUX下的行为。ORACLE版本为10.2.0.1。

讲到动态注册,跟监听密切相关,下面先看看监听的行为:

监听在启动时,会从$ORACLE_HOME/network/admin/listener.ora读取监听配置,如果该文件不存在,则监听会在主机名对应的IP和1521端口上进行监听。如果主机名在/etc/hosts里没有配置(或不能通过DNS解析---这是我的猜想,没有验证),则在等待较长一段时间后,将在所有的地址上(0.0.0.0:1521)进行监听,但此时实例并不会进行动态注册,客户端可通过主机的任意IP地址连接,但均会报ORA-12514错误。除非设置LOCAL_LISTENER参数,将本地LISTENER地址指向本机的任意一IP。

如果存在listener.ora文件,则监听会根据该文件配置内容进行启动。如果主机名在/etc/hosts没有条目,监听 启动比较慢(可能是监听起来后,在根据主机名作什么操作),因此必须要保证主机名要在/etc/hosts中有记录。

同一个网络接口(网卡)上,如果绑定了两个或以上的IP地址,则监听这样的网络接口时,最多只能使用一个主机名,比如:

[oracle@xty ~]$ cat /etc/hosts
# Do not remove the following line, or various programs
# that require network functionality will fail.
127.0.0.1       localhost.localdomain   localhost
192.168.0.114   xty
192.168.0.115   xty_vip
192.168.0.116   xty_vip2

这里xty和xty_vip对应的IP绑定在同一网卡上

cat listener.ora

# Generated by Oracle configuration tools.

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (SID_NAME = PLSExtProc)
      (ORACLE_HOME = /u01/app/oracle/oracle/product/10.2.0/db_1)
      (PROGRAM = extproc)
    )
  )

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = xty_vip )(PORT = 1521))
      (ADDRESS = (PROTOCOL = TCP)(HOST = xty )(PORT = 1521))
      (ADDRESS = (PROTOCOL = TCP)(HOST = xty_vip2 )(PORT = 1521))
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC0))
    )
  )

启动监听时,报下面的错误:

TNSLSNR for Linux: Version 10.2.0.1.0 - Production
System parameter file is /u01/app/oracle/oracle/product/10.2.0/db_1/network/admi
n/listener.ora
Log messages written to /u01/app/oracle/oracle/product/10.2.0/db_1/network/log/l
istener.log
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.115)(PORT=1521
)))
Error listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=xty)(PORT=1521)))
TNS-12542: TNS:address already in use
 TNS-12560: TNS:protocol adapter error
  TNS-00512: Address already in use
   Linux Error: 98: Address already in use

Listener failed to start. See the error message(s) above...

将listener.ora中的(ADDRESS = (PROTOCOL = TCP)(HOST = xty )(PORT = 1521))行,改为(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.114 )(PORT = 1521)),则监听能够正常启动

下面再来看一下PMON向监听注册实例的行为:

在缺省情况下(也就是没有LOCAL_LISTENER参数配置的情况下),PMON会根据主机名(hostname),查找其IP地址,通常是在/etc/hosts中找对应的条目,如果没有找到hostname的IP地址,则PMON不会注册,同时,必须是本机的IP地址,PMON才能注册。比如在/etc/hosts中将hostname对应的IP地址改为其他非本机的IP地址,PMON也不会进行注册。PMON根据hostname找到IP后,同时判断这个是本机IP,则会通过这个IP连接至监听进行注册。如果listener没有监听这个IP,则PMON也不会注册,因为通过这个IP连接不上监听。

举一个简单的例子,现有两台IBM小机,作双机热备,双机采用HACMP。在监听设置中,只监听了HA的服务IP地址,而hostname对应的IP地址为服务IP绑定的网卡上的另一个地址。在这种情况下,PMON不能进行动态注册,在客户端连接这个ORACLE服务器时,将会报ORA-12514错误。

解决上面提到的这个问题的办法,除了静态注册(本文主要讨论动态注册),还有两种办法,第一种我个人认为最好的一种,是在LISTENER上监听两个IP地址,类似于下面这样:

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = xty_vip )(PORT = 1521))
      (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.114 )(PORT = 1521))
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC0))
    )
  )

另一个办法就是设置LOCAL_LISTENER参数,假如LISTENER只监听了xty_vip(192.168.0.115)这个地址,则通过下面的命令设置LOCAL_LISTENER:

    ALTER SYSTEM SET LOCAL_LISTENER=' (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.115 )(PORT = 1521))'

这里建议使用IP地址,特别是对RAC,后文细述。

LOCAL_LISTENER使PMON改变用hostname连接LISTENER进行注册的默认行为。改而用LOCAL_LISTENER参数指定的地址连接LISTENER进行注册。当然LOCAL_LISTENER指定的IP地址必须是本机的IP地址。如果是非本机IP,则会忽略此参数,但是会从前一个已注册的监听中取消注册。

与LOCAL_LISTENER对应的参数有REMOTE_LISTENER参数。REMOTE_LISTENER使PMON在远程(即非本机)监听上进行注册,这个参数在RAC中经常使用(用于负载均衡)。

下面来作一个测试

主机1,LINUX AS4
主机2,Windows 2003,IP地址:192.168.0.100

,在主机2上启动监听:
D:\oracle\product\10.2.0\db_1\BIN>lsnrctl start

LSNRCTL for 32-bit Windows: Version 10.2.0.1.0 - Production on 02-2月 -2008 09:5
6:44

Copyright (c) 1991, 2005, Oracle.  All rights reserved.

启动tnslsnr: 请稍候...

TNSLSNR for 32-bit Windows: Version 10.2.0.1.0 - Production
写入d:\oracle\product\10.2.0\db_1\network\log\listener.log的日志信息
监听: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=dreamf)(PORT=1521)))

正在连接到 (ADDRESS=(PROTOCOL=tcp)(HOST=)(PORT=1521))
LISTENER 的 STATUS
------------------------
别名                      LISTENER
版本                      TNSLSNR for 32-bit Windows: Version 10.2.0.1.0 - Produ
ction
启动日期                  02-2月 -2008 09:56:47
正常运行时间              0 天 0 小时 0 分 3 秒
跟踪级别                  off
安全性                    ON: Local OS Authentication
SNMP                      OFF
监听程序日志文件          d:\oracle\product\10.2.0\db_1\network\log\listener

监听端点概要...
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=dreamf)(PORT=1521)))
监听程序不支持服务
命令执行成功

在主机1上启动监听:
[oracle@xty ~]$ lsnrctl start

LSNRCTL for Linux: Version 10.2.0.1.0 - Production on 01-FEB-2008 17:28:57

Copyright (c) 1991, 2005, Oracle.  All rights reserved.

Starting /u01/app/oracle/oracle/product/10.2.0/db_1/bin/tnslsnr: please wait...

TNSLSNR for Linux: Version 10.2.0.1.0 - Production
System parameter file is /u01/app/oracle/oracle/product/10.2.0/db_1/network/admi
n/listener.ora
Log messages written to /u01/app/oracle/oracle/product/10.2.0/db_1/network/log/l
istener.log
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.115)(PORT=1521
)))
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.114)(PORT=1521
)))
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC0)))

Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=xty_vip)(PORT=1521)))
STATUS of the LISTENER
------------------------
Alias                     LISTENER
Version                   TNSLSNR for Linux: Version 10.2.0.1.0 - Production
Start Date                01-FEB-2008 17:28:57
Uptime                    0 days 0 hr. 0 min. 0 sec
Trace Level               off
Security                  ON: Local OS Authentication
SNMP                      OFF
Listener Parameter File   /u01/app/oracle/oracle/product/10.2.0/db_1/network/adm
in/listener.ora
Listener Log File         /u01/app/oracle/oracle/product/10.2.0/db_1/network/log
/listener.log
Listening Endpoints Summary...
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.115)(PORT=1521)))
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.114)(PORT=1521)))
  (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC0)))
Services Summary...
Service "PLSExtProc" has 1 instance(s).
  Instance "PLSExtProc", status UNKNOWN, has 1 handler(s) for this service...
The command completed successfully
[oracle@xty ~]$

然后在主机1上启动数据库。输入命令:

alter system set remote_listener='(ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.100)(PORT=1521))';

在主机2上查看LISTENER的状态

正在连接到 (ADDRESS=(PROTOCOL=tcp)(HOST=)(PORT=1521))
LISTENER 的 STATUS
------------------------
别名                      LISTENER
版本                      TNSLSNR for 32-bit Windows: Version 10.2.0.1.0 - Produ
ction
启动日期                  02-2月 -2008 09:56:47
正常运行时间              0 天 0 小时 16 分 54 秒
跟踪级别                  off
安全性                    ON: Local OS Authentication
SNMP                      OFF
监听程序日志文件          d:\oracle\product\10.2.0\db_1\network\log\listener.log

监听端点概要...
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=dreamf)(PORT=1521)))
服务摘要..
服务 "XTY" 包含 1 个例程。
  例程 "XTY1", 状态 READY, 包含此服务的 1 个处理程序...
服务 "XTY1XDB" 包含 1 个例程。
  例程 "XTY1", 状态 READY, 包含此服务的 1 个处理程序...
服务 "XTY_XPT" 包含 1 个例程。
  例程 "XTY1", 状态 READY, 包含此服务的 1 个处理程序...
命令执行成功

在主机2上再执行lsnrctl service命令:

D:\oracle\product\10.2.0\db_1\BIN>lsnrctl service

LSNRCTL for 32-bit Windows: Version 10.2.0.1.0 - Production on 02-2月 -2008 10:
5:21

Copyright (c) 1991, 2005, Oracle.  All rights reserved.

正在连接到 (ADDRESS=(PROTOCOL=tcp)(HOST=)(PORT=1521))
服务摘要..
服务 "XTY" 包含 1 个例程。
  例程 "XTY1", 状态 READY, 包含此服务的 1 个处理程序...
    处理程序:
      "DEDICATED" 已建立:0 已拒绝:0 状态:ready
         REMOTE SERVER
         (ADDRESS=(PROTOCOL=TCP)(HOST=xty)(PORT=1521))
服务 "XTY1XDB" 包含 1 个例程。
  例程 "XTY1", 状态 READY, 包含此服务的 1 个处理程序...
    处理程序:
      "D000" 已建立:0 已被拒绝:0 当前: 0 最大: 1022 状态: ready
         DISPATCHER <machine: xty, pid: 2996>
         (ADDRESS=(PROTOCOL=tcp)(HOST=xty)(PORT=32801))
服务 "XTY_XPT" 包含 1 个例程。
  例程 "XTY1", 状态 READY, 包含此服务的 1 个处理程序...
    处理程序:
      "DEDICATED" 已建立:0 已拒绝:0 状态:ready
         REMOTE SERVER
         (ADDRESS=(PROTOCOL=TCP)(HOST=xty)(PORT=1521))
命令执行成功

主机1上的实例已经成功注册到主机2上的监听

在主机2上的TNSNAMES.ORA中有:

XTY_R =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.100)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVICE_NAME = xty)
    )
  )

在主机2上连接XTY_R(这个实例实际运行在主机1上)

D:\oracle\admin\XJ\bdump>sqlplus test/test@xty_r

SQL*Plus: Release 9.2.0.1.0 - Production on 星期六 2月 2 10:19:41 2008

Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.

ERROR:
ORA-12535: TNS: 操作超时

请输入用户名:

报了ORA-12536错误。

从上面的lsnrctl service命令可以查看到REMOTE SERVER的地址为:
(ADDRESS=(PROTOCOL=TCP)(HOST=xty)(PORT=1521))
因此客户端连接时,LISTENER判断这是个远程SERVER,会将这个地址返回给客户,客户端再去连接这个地址。但这里HOST=xty,客户端不能解析这个地址,所以就报超时错误。

但如果在主机1上的数据库中执行下面的命令:
alter system set local_listener='(ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.115)(PORT=1521))';

我们再看看主机2上的LISTENER SERVICE:

正在连接到 (ADDRESS=(PROTOCOL=tcp)(HOST=)(PORT=1521))
服务摘要..
服务 "XTY" 包含 1 个例程。
  例程 "XTY1", 状态 READY, 包含此服务的 1 个处理程序...
    处理程序:
      "DEDICATED" 已建立:0 已拒绝:0 状态:ready
         REMOTE SERVER
         (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.115)(PORT=1521))
服务 "XTY1XDB" 包含 1 个例程。
  例程 "XTY1", 状态 READY, 包含此服务的 1 个处理程序...
    处理程序:
      "D000" 已建立:0 已被拒绝:0 当前: 0 最大: 1022 状态: ready
         DISPATCHER <machine: xty, pid: 2996>
         (ADDRESS=(PROTOCOL=tcp)(HOST=xty)(PORT=32801))
服务 "XTY_XPT" 包含 1 个例程。
  例程 "XTY1", 状态 READY, 包含此服务的 1 个处理程序...
    处理程序:
      "DEDICATED" 已建立:0 已拒绝:0 状态:ready
         REMOTE SERVER
         (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.115)(PORT=1521))
命令执行成功

注意看到上面REMOTE SERVER已经变成了 (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.115)(PORT=1521))
这个时候再次连接,由成功连接到ORACLE服务器上:

D:\oracle\admin\XJ\bdump>sqlplus test/test@xty_r

SQL*Plus: Release 9.2.0.1.0 - Production on 星期六 2月 2 10:39:25 2008

Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.

连接到:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options

SQL>

在RAC中,服务器开启了负载均衡,则客户端有时连接时会出现ORA-12514错误,这里需要设置LOCAL_LISTENER参数,以解决该问题。

注意:在通过REMOTE_LISTENER参数向远程监听注册时,本地的LISTENER也需要处于启动状态,否则监听中服务的状态为BLOCKED状态:

正在连接到 (ADDRESS=(PROTOCOL=tcp)(HOST=)(PORT=1521))
服务摘要..
服务 "XTY" 包含 1 个例程。
  例程 "XTY1", 状态 READY, 包含此服务的 1 个处理程序...
    处理程序:
      "DEDICATED" 已建立:1 已拒绝:0 状态:blocked
         REMOTE SERVER
         (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.115)(PORT=1521))
服务 "XTY1XDB" 包含 1 个例程。
  例程 "XTY1", 状态 READY, 包含此服务的 1 个处理程序...
    处理程序:
      "D000" 已建立:0 已被拒绝:0 当前: 0 最大: 1022 状态: ready
         DISPATCHER <machine: xty, pid: 2996>
         (ADDRESS=(PROTOCOL=tcp)(HOST=xty)(PORT=32801))
服务 "XTY_XPT" 包含 1 个例程。
  例程 "XTY1", 状态 READY, 包含此服务的 1 个处理程序...
    处理程序:
      "DEDICATED" 已建立:1 已拒绝:0 状态:blocked
         REMOTE SERVER
         (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.115)(PORT=1521))
命令执行成功

而这个时候客户端连接报ORA-12516错误

另外,数据库实例版本必须与LISTENER版本兼容,否则不能进行动态注册。

本文主要是通过实验和分析网络包,然后进行总结的结果,没有参考相关的理论文档。有所错误在所难免,欢迎讨论。

众所周知,如果一个库没有设置为force logging,而这个库在归档模式下,对表的插入操作如果采用APPEND模式,并且表设置为nologging则不会为插入的数据产生日志。那么对LOB列的存储也设置为NOLOGGING,会产生什么样的结果?

测试环境:Oracle 9.2.0.1 for Win,非归档模式

create table test1.t1(id int not null, out_row clob)
lob(out_row) store as (disable storage in row nocache nologging);

关闭数据库,备份数据文件users01.dbf

启动数据库,将表中插入数据:

insert into test1.t1 select rownum,rpad('x',5000,'x') from dba_objects where rownum<=100;

select * from test1.t1;

能够正常返回100行数据。

关闭数据库,用备份的文件还原文件users01.dbf,启动数据库时报错:

数据库装载完毕。
ORA-01113: ?? 5 ??????
ORA-01110: ???? 5: 'D:\ORACLE\ORADATA\XJ\USERS01.DBF'

SQL> recover datafile 5;
完成介质恢复。

SQL> alter session set nls_language=american;

Session altered.
SQL> alter database open;

Database altered.

SQL> select count(*) from test1.t1;

  COUNT(*)
----------
       100

SQL> select * from test1.t1;
ERROR:
ORA-01578: ORACLE data block corrupted (file # 5, block # 61)
ORA-26040: Data block was loaded using the NOLOGGING option
ORA-01110: data file 5: 'D:\ORACLE\ORADATA\XJ\USERS01.DBF'

由此可以看出,在此前的插入操作中,没有对LOB数据产生日志。

我们将test1.t1删除,再重新创建,只是将LOB设置为LOGGING。

SQL> drop table test1.t1;

表已丢弃。

SQL> create table test1.t1 (id number not  null,out_row clob)
  2  lob (out_row) store as (disable storage in row nocache logging);

重复上步实验过程,发现LOGGING模式下的LOB能够正常恢复。

将数据库设置为归档模式,重复以上测试过程,发现NOLOGGING模式下的LOB仍然不能恢复。

将数据库设置为FORCE LOGGING模式,重复以上测试过程,发现NOLOGGING模式下的LOB能够正常恢复。

经过进一步测试,对LOB字段进行UPDATE也会产生上述实验结果。

在这个测试中使用了一个比较“笨”的办法。观察LOB列是否产生日志,可以观察redo size和分析日志文件进行。在此不在细述。

经过测试,发现IN ROW的LOB列与表中的其他列数据是一致的LOGGING行为。

注意:LOB列如果设置为CACHE,则只能是LOGGING模式。

由此引出一个问题,由于对LOB列设置为NOLOGGING,虽然能够提高数据插入速度,为数据恢复设置了一道难关。需要此种情况下的备份恢复策略,或者将LOB列设置为LOGGING模式。