大家在管理ORACLE数据库时,通常使用PROFILE对用户密码设置期限,但是需要注意应用系统连接的用户,对于密码期限设置,应谨慎处理。

首先我们看看怎么样为用户设置密码有效期:

在测试例子中,我们使用TEST用户进行测试,密码为TEST,先看看用户的状态。

SQL> select account_status,lock_date,expiry_date,created,profile from dba_users where username='TEST';

ACCOUNT_STATUS LOCK_DATE EXPIRY_DATE CREATED PROFILE
-------------- ---------- ----------- ------------------- -------
OPEN 2008-03-27 15:29:18 DEFAULT

SQL> select ctime,ptime,exptime,ltime,spare6 from user$ where name='TEST';

CTIME PTIME EXPTIME LTIME SPARE6
------------------- ------------------- ------------------- ------------------- -------------------
2008-03-27 15:29:18 2008-03-27 15:29:18

在user$数据字典中,有几个字段是dba_users中看不到的。比如PTIME(应该就是密码设置或修改的时间),这里PTIME=CREATED(因为用户创建后没有改变过密码)

下面我们将创建profile,将密码有效期设置为10天,并将用户TEST的PROFILE设置为创建的test_profile:

SQL> create profile test_profile limit password_life_time 10 password_grace_time 0;
配置文件已创建
SQL> alter user test profile test_profile;
用户已更改。

再看看用户数据:

SQL> select account_status,lock_date,expiry_date,created,profile from dba_users where username='TEST';

ACCOUNT_STATUS LOCK_DATE EXPIRY_DATE CREATED PROFILE
------------------------------ ------------------- ------------------- ------------------- ------------
OPEN 2008-04-06 15:29:18 2008-03-27 15:29:18 TEST_PROFILE

SQL> select ctime,ptime,exptime,ltime,spare6 from user$ where name='TEST';

CTIME PTIME EXPTIME LTIME SPARE6
------------------- ------------------- ------------------- ------------------- -------------------
2008-03-27 15:29:18 2008-03-27 15:29:18

注意到除了PROFILE列从DEFAULT变为了TEST_PROFILE,EXPIRY_DATE改变了,正好是PTIME+10。注意在USER$表中,EXPTIME为空,DBA_USERS中的EXPIRY_TIME是通过PTIME和PROFILE计算出来的。因此EXPIRY_TIME为密码将要到期的时间。

现在为用户设置好了PROFILE,新开一个SQLPLUS会话,用TEST用户登录:

SQL> connect test/test
ERROR:
ORA-28001: the password has expired

更改test的口令
新口令:

这个时候可以修改用户的密码。但是如果是应用软件没有对ORA-28001错误进行处理,则不能进行连接。这个时候我们看看用户的状态:

SQL> select account_status,lock_date,expiry_date,created,profile from dba_users where username='TEST';

ACCOUNT_STATUS LOCK_DATE EXPIRY_DATE CREATED PROFILE
------------------------------ ------------------- ------------------- ------------------- ------------
EXPIRED 2008-03-27 15:29:18 TEST_PROFILE
SQL> select account_status,lock_date,expiry_date,created,profile from dba_users where username='TEST';

ACCOUNT_STATUS LOCK_DATE EXPIRY_DATE CREATED PROFILE
------------------------------ ------------------- ------------------- ------------------- ------------
EXPIRED 2008-03-27 15:29:18 TEST_PROFILE

可以看到用户状态为EXPIRED。这个状态是记录在数据字典USER$中的,如果此时把用户的PROFILE改回为DEFAULT,这个状态仍然不会改变。

SQL> alter user test profile default;

用户已更改。

SQL> select account_status,lock_date,expiry_date,created,profile from dba_users where username='TEST';

ACCOUNT_STATUS LOCK_DATE EXPIRY_DATE CREATED PROFILE
------------------------------ ------------------- ------------------- ------------------- ------------
EXPIRED 2008-03-27 15:29:18 DEFAULT

用户状态仍然为EXPIRED。这个时候,如果应用连接不上数据库,只有DBA手工干预了。对于状态为LOCKED的用户,使用alter user user_name account unlock即可。对于EXPIRED状态的用户,没有UNEXPIRE的命令,只有通过修改用户密码来重设状态:

SQL> alter user test identified by test;

用户已更改。

SQL> select account_status,lock_date,expiry_date,created,profile from dba_users where username='TEST';

ACCOUNT_STATUS LOCK_DATE EXPIRY_DATE CREATED PROFILE
------------------------------ ------------------- ------------------- ------------------- ------------
OPEN 2008-03-27 15:29:18 DEFAULT

可以看到用户状态已经恢复正常。

SQL> select ctime,ptime,exptime,ltime,spare6 from user$ where name='TEST';

CTIME PTIME EXPTIME LTIME SPARE6
------------------- ------------------- ------------------- ------------------- ------------------
2008-03-27 15:29:18 2008-08-13 23:16:42

用户的密码设置时间也发生了变化。

如果在某些情况下,不知道用户的密码,可以通过下面的办法来设置密码:

SQL> select password from dba_users where username='TEST';

PASSWORD
------------------------------
7A0F2B316C212D67

SQL> alter user test identified by values '7A0F2B316C212D67';

用户已更改。

总结:对于为用户设置了密码有效期限的用户,应用软件应能够处理ORA-28001错误,或者DBA应定期对密码进行重设。否则不要为用户密码设置期限,当然对于DBA帐号,为安全起见,通过PROFILE强制要求用户定期修改密码,并且可以要求每次使用的密码不能使用以前使用的密码。

虽然大部分时候都是在用Oracle9i,就算用10g,新特性也用得不多,但现在连11g都出来一年了,再这样下去就成老古董啦。所以如今从头把concepts来看,不过这次是10g的版本。数年之前,曾经看过两遍8i的concepts,9i的concepts也读过两遍,看来这10g的concepts只需要一遍就够了。

Oracle的经典书籍何其多,我也仔细看过数本,但我觉得最实在的还是Oracle的官方文档。不管怎么忙,一天至少要看10页,不是简单的看,而是要真正理解。希望从concept这本最经典的书中挖掘出以往不曾注意到的价值。

 在上一篇文章(Oracle的段类型 Part I)中,我们得到了Oracle9i中所有的段类型,下面我们将对每一种段类型进行一个简单的说明:
TABLE:这是最常见的段类型,普通表(即非CLUSTER),没有分区,则每个表有一个类型为TABLE的段。
INDEX:这是除了TABLE之外最常见的段类型,表的普通索引,没有分区,则每个索引有一个类型为INDEX的段。除了表上的普通索引之外,INDEX CLUSTER上的索引也是INDEX段,并且在INDEX CLUSTER上必须有一个索引(HASH CLUSTER不要求建索引)。注意IOT表的段类型为INDEX段,而不是TABLE段:
SQL> create table t2 ( object_id number primary key,object_name varchar2(100))
  2  organization index;
SQL> select owner,segment_type,segment_name,header_file,header_block from dba_segments where segment_name='T2';

未选定行

SQL> select index_name from user_indexes where table_name='T2';

INDEX_NAME
------------------------------
SYS_IOT_TOP_29668

SQL> select owner,segment_type,segment_name from dba_segments where segment_name='SYS_IOT_TOP_29668';

OWNER      SEGMENT_TYPE    SEGMENT_NAME
---------- --------------- ----------------------------------------
SYS        INDEX           SYS_IOT_TOP_29668
注意IOT表的溢出段是TABLE类型的段:
SQL> create table iot
  2    (  x    int,
  3       y    date,
  4      z    varchar2(2000),
  5      constraint iot_pk primary key (x)
  6   )
  7   organization index
  8   pctthreshold 10
  9   overflow;

表已创建。
SQL> select owner,segment_type,segment_name from dba_segments where  owner='TEST';

OWNER      SEGMENT_TYPE    SEGMENT_NAME
---------- --------------- ----------------------------------------
TEST       TABLE           SYS_IOT_OVER_29670
TEST       INDEX           IOT_PK

 

TABLE PARTITION和TABLE SUBPARTITION:表分区,每个分区或子分区都有一个段。
INDEX PARTITION和INDEX SUBPARTITION:索引分区,每个分区或子分区都有一个段。
CLUSTER:每个CLUSTER有一个CLUSTER段。一个CLUSTER中可以存储一个或多个表。由于CLUSTER不能分区,所以没有CLUSTER PARTITION这样的段。
LOBINDEX:表的每个LOB字段,有一个LOBINDEX段。注意对于分区表的LOB字段,每个分区上的LOB字段均会有LOBINDEX段,但是段类型为INDEX PARTITION或INDEX SUBPARTITION,这是一个特殊情况(不知道ORACLE为什么这样,从视图定义上看sys.indpart$和sys.indsubpart$没有type#字段)。
LOBSEGMENT、LOB PARTITION、LOB SUBPARTITION:表中的每个LOB字段,有LOBSEGMENT字段,如果表进行了分区,则在每个分区上相应有LOB PARTITION和LOB SUBPARTITION:
SQL> create table t
  2  ( id int primary key,
  3    txt clob
  4  )
  5  /
SQL> select owner,segment_type,segment_name from dba_segments where  owner='TEST';

OWNER      SEGMENT_TYPE    SEGMENT_NAME
---------- --------------- ----------------------------------------
TEST       TABLE           T
TEST       LOBINDEX        SYS_IL0000029682C00002$$
TEST       INDEX           SYS_C002632
TEST       LOBSEGMENT      SYS_LOB0000029682C00002$$

注意:虽然BFILE可以作为LOB类型进行处理,但存储没有LOBINDEX和LOBSEGMENT字段:
SQL> create table t
  2  ( id int primary key,
  3    thefile bfile
  4  )
  5  /
SQL> select owner,segment_type,segment_name from dba_segments where  owner='TEST';

OWNER      SEGMENT_TYPE    SEGMENT_NAME
---------- --------------- ----------------------------------------
TEST       TABLE           T
TEST       INDEX           SYS_C002633

可以看到没有任何与BFILE相关的单独的段。

ROLLBACK:就是8i及以前的回滚段,在9i以及以后的版本中,即使使用了自动撤销段管理,仍然会有一个SYSTEM回滚段。
TYPE2 UNDO:这就是9i及以后的“撤销段”,跟ROLLBACK段类似。我们仍然习惯于叫回滚段。
DEFERRED ROLLBACK:延迟回滚段。如果一个表空间OFFLINE时,表空间上的对象存在活动事务,则会在SYSTEM表空间中创建延迟回滚段,以便在表空间ONLINE能够回滚:
SQL> insert into t select * from dba_objects where rownum<=10;

已创建10行。
SQL> alter tablespace tools offline;

表空间已更改。
SQL> select owner,segment_type,segment_name from dba_segments where  segment_type like '%DEF%';

OWNER      SEGMENT_TYPE                   SEGMENT_NAME
---------- ------------------------------ ----------------------------------------
SYS        DEFERRED ROLLBACK              1.84337

TEMPORARY:临时段。除了磁盘排序产生临时段之外,临时表也会有临时段。另外,在CTAS过程中,如果SQL还没有最终完成,这个时候的表对应的段为TEMPORARY表,只有在SQL执行的最后将TEMPORARY段改为TABLE段。比如:
SQL> create table t2 as select * from dba_objects;
在执行上面语句的同时,执行:
SQL> select segment_type,owner,segment_name from dba_segments where segment_type='TEMPORARY';

SEGMENT_TYPE       OWNER                          SEGMENT_NAME
------------------ ------------------------------ --------------------------------------------
TEMPORARY          SYS                            1.84337

可以看到临时段,在CTAS执行完之后,我们可以看到:

SQL> select segment_type,owner,segment_name from dba_segments where segment_type='TEMPORARY';

未选定行
SQL> select header_file,header_block from dba_segments where owner=USER and segment_name='T2';

HEADER_FILE HEADER_BLOCK
----------- ------------
          1        84337

可以看到,之前的临时段(其段名为一个特别的名字1.84337,段头的文件号和块号),与CTAS后的表的段头一致。
另外在表和索引的MOVE、REBUILD阶段也会有临时段。所以临时段不一定是在临时表中,在普通的表空间中也可能会存在。
 注意在排序段和临时表的段在并没有在DBA_SEGMENTS视图,而是在V$TEMPSEG_USAGE视图中。

CACHE:这是一个特殊的段,为Oracle的自举(bootstrap)段。
SQL> select owner,segment_type,segment_name,header_file,header_block from dba_segments where segment_type='CACHE';

OWNER      SEGMENT_TYPE    SEGMENT_NAME    HEADER_FILE HEADER_BLOCK
---------- --------------- --------------- ----------- ------------
SYS        CACHE           1.833                     1          833

我们通过DUMP数据文件头可以发现:
 FILE HEADER:
    Software vsn=153092096=0x9200000, Compatibility Vsn=134217728=0x8000000
    Db ID=2968647772=0xb0f1f85c, Db Name='XJ'
    Activation ID=0=0x0
    Control Seq=761=0x2f9, File size=128000=0x1f400
    File Number=1, Blksiz=4096, File Type=3 DATA
Tablespace #0 - SYSTEM  rel_fn:1
Creation   at   scn: 0x0000.00000009 05/12/2002 16:20:42
Backup taken at scn: 0x0000.00000000 01/01/1988 00:00:00 thread:0
 reset logs count:0x26ce9ece scn: 0x0000.000a65e0 recovered at 07/19/2008 21:23:10
 status:0x4 root dba:0x00400341 chkpt cnt: 272 ctl cnt:271

root dba转换为文件号和块号则为1.833,正好是类型为CACHE的段头。Oracle通过文件号为1的文件头的root dba定位到自举对象,然后得到obj$等核心对象所在位置,来进行启动。
NESTED TABLE:嵌套表的段,以面举例子说明:
SQL> create or replace type emp_type
  2  as object
  3  (empno       number(4),
  4   ename       varchar2(10),
  5   job         varchar2(9),
  6   mgr         number(4)
  7   );
  8  /

类型已创建。

SQL> create or replace type emp_tab_type
  2   as table of emp_type;
  3  /

类型已创建。
SQL> create table dept_and_em
  2  (deptno number(2) primary key,
  3   dname     varchar2(14),
  4   loc       varchar2(13),
  5   emps      emp_tab_type,
  6   emps2      emp_tab_type
  7  )
  8  nested table emps store as emps_nt
  9  nested table emps2 store as emps_nt2;

表已创建。
SQL> select owner,segment_type,segment_name from dba_segments where  owner='TEST';

OWNER      SEGMENT_TYPE    SEGMENT_NAME
---------- --------------- ----------------------------------------
TEST       NESTED TABLE    EMPS_NT
TEST       NESTED TABLE    EMPS_NT2
TEST       TABLE           DEPT_AND_EM
TEST       INDEX           SYS_C002629
TEST       INDEX           SYS_C002630
TEST       INDEX           SYS_C002631

这里看到有两个类型为NESTED TABLE的段。另外除了主键之外,每个NESTED TABLE字段上还有一个索引(实际上是每个NESTED TABLE字段对应一具隐含字段,上面建有索引)。
 

Oracle数据库中有多少类型的段,除了常见的TABLE、INDEX之外还有哪些?下面通过Oracle9i的数据字典来探讨Oracle的段类型。

SQL> select distinct segment_type from dba_segments;

SEGMENT_TYPE
------------------
CACHE
CLUSTER
INDEX
INDEX PARTITION
LOBINDEX
LOBSEGMENT
NESTED TABLE
ROLLBACK
TABLE
TABLE PARTITION
TYPE2 UNDO

然而在DBA_SEGMENTS视图中,不一定包含了所有的段类型,我们从DBA_SEGMENTS的定义中去寻找Oracle的段类型。

SQL> select text from dba_views where view_name='DBA_SEGMENTS';

TEXT
----------------------------------------------------------------------------------------------------
select owner, segment_name, partition_name, segment_type, tablespace_name,
       header_file, header_block,
       dbms_space_admin.segment_number_blocks(tablespace_id, relative_fno,
       header_block, segment_type_id, buffer_pool_id, segment_flags,
       segment_objd, blocks)*blocksize,
       dbms_space_admin.segment_number_blocks(tablespace_id, relative_fno,
       header_block, segment_type_id, buffer_pool_id, segment_flags,
       segment_objd, blocks),
       dbms_space_admin.segment_number_extents(tablespace_id, relative_fno,
       header_block, segment_type_id, buffer_pool_id, segment_flags,
       segment_objd, extents),
       initial_extent, next_extent, min_extents, max_extents, pct_increase,
       freelists, freelist_groups, relative_fno,
       decode(buffer_pool_id, 0, 'DEFAULT', 1, 'KEEP', 2, 'RECYCLE', NULL)
from sys_dba_segs

 SQL> select text from dba_views where view_name='SYS_DBA_SEGS';

TEXT
--------------------------------------------------------------------------
select u.name, o.name, o.subname,
       so.object_type, s.type#,
       ts.ts#, ts.name, ts.blocksize,
       f.file#, s.block#,
       s.blocks * ts.blocksize, s.blocks, s.extents,
       s.iniexts * ts.blocksize,
       decode(bitand(ts.flags, 3), 1, to_number(NULL),
                                      s.extsize * ts.blocksize),
       s.minexts, s.maxexts,
       decode(bitand(ts.flags, 3), 1, to_number(NULL),
                                      s.extpct),
       decode(bitand(ts.flags, 32), 32, to_number(NULL),
              decode(s.lists, 0, 1, s.lists)),
       decode(bitand(ts.flags, 32), 32, to_number(NULL),
              decode(s.groups, 0, 1, s.groups)),
       s.file#, s.cachehint, NVL(s.spare1,0), o.dataobj#
from sys.user$ u, sys.obj$ o, sys.ts$ ts, sys.sys_objects so, sys.seg$ s,
     sys.file$ f
where s.file# = so.header_file
  and s.block# = so.header_block
  and s.ts# = so.ts_number
  and s.ts# = ts.ts#
  and o.obj# = so.object_id
  and o.owner# = u.user#
  and s.type# = so.segment_type_id
  and o.type# = so.object_type_id
  and s.ts# = f.ts#
  and s.file# = f.relfile#
select u.name, un.name, NULL,
       decode(s.type#, 1, 'ROLLBACK', 10, 'TYPE2 UNDO'), s.type#,
       ts.ts#, ts.name, ts.blocksize, f.file#, s.block#,
       s.blocks * ts.blocksize, s.blocks, s.extents,
       s.iniexts * ts.blocksize, s.extsize * ts.blocksize, s.minexts,
       s.maxexts, s.extpct,
       decode(bitand(ts.flags, 32), 32, to_number(NULL),
         decode(s.lists, 0, 1, s.lists)),
       decode(bitand(ts.flags, 32), 32, to_number(NULL),
         decode(s.groups, 0, 1, s.groups)),
       s.file#, s.cachehint, NVL(s.spare1,0), un.us#
from sys.user$ u, sys.ts$ ts, sys.undo$ un, sys.seg$ s, sys.file$ f
where s.file# = un.file#
  and s.block# = un.block#
  and s.ts# = un.ts#
  and s.ts# = ts.ts#
  and s.user# = u.user#
  and s.type# in (1, 10)
  and un.status$ != 1
  and un.ts# = f.ts#
  and un.file# = f.relfile#
union all
select u.name, to_char(f.file#) || '.' || to_char(s.block#), NULL,
       decode(s.type#, 2, 'DEFERRED ROLLBACK', 3, 'TEMPORARY',
                      4, 'CACHE', 9, 'SPACE HEADER', 'UNDEFINED'), s.type#,
       ts.ts#, ts.name, ts.blocksize,
       f.file#, s.block#,
       s.blocks * ts.blocksize, s.blocks, s.extents,
       s.iniexts * ts.blocksize,
       decode(bitand(ts.flags, 3), 1, to_number(NULL),
                                      s.extsize * ts.blocksize),
       s.minexts, s.maxexts,
       decode(bitand(ts.flags, 3), 1, to_number(NULL),
                                      s.extpct),
       decode(bitand(ts.flags, 32), 32, to_number(NULL),
         decode(s.lists, 0, 1, s.lists)),
       decode(bitand(ts.flags, 32), 32, to_number(NULL),
         decode(s.groups, 0, 1, s.groups)),
       s.file#, s.cachehint, NVL(s.spare1,0), s.hwmincr
from sys.user$ u, sys.ts$ ts, sys.seg$ s, sys.file$ f
where s.ts# = ts.ts#
  and s.user# = u.user#
  and s.type# not in (1, 5, 6, 8, 10)
  and s.ts# = f.ts#
  and s.file# = f.relfile#

SQL> select text from dba_views where view_name='SYS_OBJECTS';

TEXT
--------------------------------------------------------------------------------------
select decode(bitand(t.property, 8192), 8192, 'NESTED TABLE', 'TABLE'), 2, 5,
       t.obj#, t.file#, t.block#, t.ts#
from sys.tab$ t
where bitand(t.property, 1024) = 0               /* exclude clustered tables */
union all
select 'TABLE PARTITION', 19, 5,
       tp.obj#, tp.file#, tp.block#, tp.ts#
from sys.tabpart$ tp
union all
select 'CLUSTER', 3, 5,
       c.obj#, c.file#, c.block#, c.ts#
from sys.clu$ c
union all
select decode(i.type#, 8, 'LOBINDEX', 'INDEX'), 1, 6,
       i.obj#, i.file#, i.block#, i.ts#
from sys.ind$ i
where i.type# in (1, 2, 3, 4, 6, 7, 8, 9)
union all
select 'INDEX PARTITION', 20, 6,
       ip.obj#, ip.file#, ip.block#, ip.ts#
from sys.indpart$ ip
union all
select 'LOBSEGMENT', 21, 8,
       l.lobj#, l.file#, l.block#, l.ts#
from sys.lob$ l
union all
select 'TABLE SUBPARTITION', 34, 5,
       tsp.obj#, tsp.file#, tsp.block#, tsp.ts#
       from sys.tabsubpart$ tsp
union all
select 'INDEX SUBPARTITION', 35, 6,
       isp.obj#, isp.file#, isp.block#, isp.ts#
from sys.indsubpart$ isp
union all
select decode(lf.fragtype$, 'P', 'LOB PARTITION', 'LOB SUBPARTITION'),
       decode(lf.fragtype$, 'P', 40, 41), 8,
       lf.fragobj#, lf.file#, lf.block#, lf.ts#
from sys.lobfrag$ lf

因此,从以上几个视图的定义中可以看到,Oracle9i中有如下的段类型:
NESTED TABLE
TABLE
TABLE PARTITION
CLUSTER
LOBINDEX
INDEX
INDEX PARTITION
LOBSEGMENT
TABLE SUBPARTITION
INDEX SUBPARTITION
LOB PARTITION
LOB SUBPARTITION
ROLLBACK
TYPE2 UNDO
DEFERRED ROLLBACK
TEMPORARY
CACHE
SPACE HEADER
UNDEFINED

 

某客户的一个表在进行分析和建索引一段时间时,均报ORA-8103错误。

让我们先看看ORA-8103错误是个什么样的错误?

[oracle@xty ~]$ oerr ora 8103
08103, 00000, "object no longer exists"
// *Cause: The object has been deleted by another user since the operation
// began, or a prior incomplete recovery restored the database to
// a point in time during the deletion of the object.
// *Action: Delete the object if this is the result of an incomplete
// recovery.

从上面的信息来看,引起这个错误的并不是由于什么对象被删除或者不完全恢复引起的。看来剩下的可能就是BUG或者是坏块了。

select /*+ parallel(a 6) no_index(a) */
count(lrrq) from hz2004.HJXX_RYZPXXB a

由于表很大,100g左右,因此等了好长一段时间,就报了ORA-8103错误。由于不同的SQL语句(建索引、分析表和查询表数据)都报了这个错误,基本可以确定是坏块引起的问题。

由于表中有long raw类型,因此不能通过从主键取得ROWID再根据ROWID读到值以后插入新表,由于查询表有问题,导出也不会成功。因此决定想办法来修复这个问题:

首先要确定是哪个块出现问题:

SQL> alter session set max_dump_file_size=unlimited;
SQL> alter session set db_file_multiblock_read_count=1;
SQL> alter session set events ‘immediate trace name trace_buffer_on level 1048576′;
SQL> alter session set events ‘10200 trace name context forever, level 1′;
SQL> select /*+ no_index(a */ count(*) from hz2004.hjxx_ryzpxxb a;

(注:经测试,在10g中,需要去掉trace_buffer_on那一个语句才会有trace,否则没有trace文件)
再次报错时,检查trace文件,发现如下的信息:

Consistent read started for block 11 : 09c63a9e
env: (scn: 0×0886.245ea878 xid: 0×0000.000.00000000
uba: 0×00000000.0000.00 statement num=0 parent xid:
xid: 0×0000.000.00000000
scn: 0×0000.00000000 0sch: scn: 0×0000.00000000)

从trace文件中可以发现在读文件号39块号408222时报错。

执行:

select * from hz2004.hjxx_ryzpxxb
where rowid=dbms_rowid.rowid_create(1,7415,39,408222,0);

报ORA-08103错误,可以确认是39号文件408222块坏。

然而用dbv命令检查39号文件,没有发现坏块。

通过dd取出39号文件408222块,发现该块居然是一个未格式化的块,但是该块在表的High water mark以下。单从该块来说是一个好的块,所以dbv检查并不会报坏块。

既然如此,我就故意做成一个坏块,然后让oracle跳过这个坏块。关闭库之后,用dd把坏块复制出来保存在一个文件,然后修改该块,使之成为一个坏块(很多的方法,这里我的方法就是修改其checksum值)。然后用dd复制回去。重启库之后,用 dbms_repair.skip_corrupt_blocks过程设置表在读取数据时,跳过坏块。

再次读取该表的所有数据,不再报错。

客户进行建索引和分析时也不再报错,顺利进行。

当然后续的工作,还是需要将数据导出来再导入,这样更稳妥。

在Oracle 10g 的database concepts上提到,Oracle 10g的最大容量是8exabytes(EB),1EB=1024PB=1048576TB,这是一个相当大的数据了。还没有听说过单个库的容量达到这么大的。(虽然现实中有达到EB级别的数据量的,但那是很多个库的数据量的合计,而不是单个库。)

我们看看这8EB是怎么计算来的:

Oracle 10g的数据文件最多可以有64K个,对于单个数据文件来说,BigFile表空间的数据文件可以达到2的32次方个块,也就是4G块(因为rdba为32位)。每个块的最大大小为32K,因此一个BigFile表空间数据文件的最大大小为4G*32K=128T。整个库的最大大小即为64K(数据文件数)* 128T(单个文件最大容量)=8192PB=8EB

先看看Oracle文档上对db_block_checking参数的说明:

DB_BLOCK_CHECKINGcontrols whether Oracle performs block checking for data blocks. When this parameter is set totrue, Oracle performs block checking for all data blocks. When it is set tofalse, Oracle does not perform. block checking for blocks in the user tablespaces. However, block checking for theSYSTEMtablespace is always turned on.

Oracle checks a block by going through the data on the block, making sure it is self-consistent. Block checking can often prevent memory and data corruption. Block checking typically causes 1% to 10% overhead, depending on workload. The more updates or inserts in a workload, the more expensive it is to turn on block checking. You should set

DB_BLOCK_CHECKING to true

if the performance overhead is acceptable

从文档中可以看到,DB_BLOCK_CHECKING参数主要是用于数据块的逻辑(一致)检查(但只是块内,不包括块间的逻辑检查,比如索引项目的ROWID指向的是不存在的行等)。主要用于防止在内存中损坏或数据损坏。由于是逻辑检查,因此引起的额外负荷比较高,甚至可以达到10%,因此对于一个繁忙的系统,特别是插入或更新操作很多的系统,性能影响是比较明显的。

该参数对SYSTEM表空间始终是处于“打开”状态,而不管该参数是否设置为FALSE。

下面再看看db_block_checksum参数的说明:

DB_BLOCK_CHECKSUMdetermines whether DBWnand the direct loader will calculate achecksum(a number calculated from all the bytes stored in the block) and store it in the cache header of every data block when writing it to disk. Checksums are verified when a block is read-only if this parameter istrueand the last write of the block stored a checksum. In addition, Oracle gives every log block a checksum before writing it to the current log.

If this parameter is set tofalse, DBWncalculates checksums only for theSYSTEMtablespace, but not for user tablespaces.

Checksums allow Oracle to detect corruption caused by underlying disks, storage systems, or I/O systems. Turning on this feature typically causes only an additional 1% to 2% overhead. Therefore, Oracle Corporation recommends that you setDB_BLOCK_CHECKSUM to true

 

可以看到,DB_BLOCK_CHECKSUM只是在写入(DBWn常规写法或用户进程直接路径写入),根据一个CHECKSUM算法,计算数据块的校验和。然后写入数据块的一个特定位置(CACHE HEADER,具体是块的16-17字节,以0字节起算)。在读取块时,再进行检验。主要是防止IO硬件和IO子系统的错误。

 

CHECKSUM的算法只是根据块的字节值计算一个效验和,因此算法比较简单,引起的系统额外负荷通常在1%-2%

实际上,即使将该参数设为TRUE,将数据块(包括SYSTEM表空间)的16-17字节清0,同时将15字节(flag),第3位(即值为16进制 0x04)清为0,则在块读取时也不会做CHECKSUM检查。如果该参数为FALSE,对于除SYSTEM的其他表空间,如果原来有CHECKSUM 值,将15-16字节清0也不会做CHECKSUM检查。

在前一篇文章(Oracle的文件号、相对文件号及其他)中,我们提到Oracle数据文件的相对文件号为1-1023。然而从10g开始,可以使用大文件(BIGFILE)的表空间,这种表空间只能使用1个数据文件。之所以称为大文件,是因为这种文件可以很“大”,最多可以有4G个块和128TB的大小。

大文件表空间的数据文件,其相对文件号都统一为1024,用下面的测试可以验证:

SQL*Plus: Release 10.2.0.1.0 - Production on Thu Apr 3 23:11:51 2008

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

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

SQL> select file_id,relative_fno,file_name,tablespace_name from dba_data_files;

   FILE_ID RELATIVE_FNO FILE_NAME                                TABLESPACE_NAME
---------- ------------ ---------------------------------------- --------------------
         1            1 /u02/oradata/xty/system01.dbf            SYSTEM
         2            2 /u02/oradata/xty/undotbs01.dbf           UNDOTBS1
         3            3 /u02/oradata/xty/sysaux01.dbf            SYSAUX
         4            4 /u02/oradata/xty/users01.dbf             USERS

SQL> create bigfile tablespace test1 datafile '/u02/oradata/xty/test01.dbf' size 2m;

Tablespace created.

SQL> select file_id,relative_fno,file_name,tablespace_name from dba_data_files;

   FILE_ID RELATIVE_FNO FILE_NAME                                TABLESPACE_NAME
---------- ------------ ---------------------------------------- --------------------
         1            1 /u02/oradata/xty/system01.dbf            SYSTEM
         2            2 /u02/oradata/xty/undotbs01.dbf           UNDOTBS1
         3            3 /u02/oradata/xty/sysaux01.dbf            SYSAUX
         4            4 /u02/oradata/xty/users01.dbf             USERS
         5         1024 /u02/oradata/xty/test01.dbf              TEST1

SQL> create bigfile tablespace test2 datafile '/u02/oradata/xty/test02.dbf' size 2m;

Tablespace created.

SQL> select file_id,relative_fno,file_name,tablespace_name from dba_data_files;

   FILE_ID RELATIVE_FNO FILE_NAME                                TABLESPACE_NAME
---------- ------------ ---------------------------------------- --------------------
         1            1 /u02/oradata/xty/system01.dbf            SYSTEM
         2            2 /u02/oradata/xty/undotbs01.dbf           UNDOTBS1
         3            3 /u02/oradata/xty/sysaux01.dbf            SYSAUX
         4            4 /u02/oradata/xty/users01.dbf             USERS
         5         1024 /u02/oradata/xty/test01.dbf              TEST1
         6         1024 /u02/oradata/xty/test02.dbf              TEST2

6 rows selected.

可以看到所有的“大”文件,其相对文件号均为1024。

每个大文件表空间只能有一个数据文件,尝试对大文件表空间增加数据文件,则会出现错误:

SQL> alter tablespace test1 add datafile '/u02/oradata/xty/test011.dbf' size 2m;
alter tablespace test1 add datafile '/u02/oradata/xty/test011.dbf' size 2m
*
ERROR at line 1:
ORA-32771: cannot add file to bigfile tablespace

Oracle10g 在AIX 5.3上的安装

今天无事,在单位的测试机上,安装了一下Oracle10g

环境:AIX 5.3 ML06 ,数据库版本 : Oracle 10g for AIX 5L 64bit

简单记录了一下安装过程:

   检查硬件配置
   检查内存:
   # lsattr -El sys0 -a realmem
   realmem 2097152 Amount of usable physical memory in Kbytes False
   检查交换空间
   # lsps -a
   Page Space      Physical Volume   Volume Group    Size %Used Active  Auto  Type
   hd6             hdisk0            rootvg         512MB     1   yes   yes    lv
   检查临时文件目录(至少应有400M空闲空间)
   df -k /tmp
   Filesystem    1024-blocks      Free %Used    Iused %Iused Mounted on
   /dev/hd3            65536     63984    3%       16     1% /tmp  
   检查操作系统位数
   # getconf HARDWARE_BITMODE
   64
   检查操作系统版本号
   # oslevel -r
   5300-06
   检查安装的软件包:
   # lslpp -l bos.adt.base bos.adt.lib bos.adt.libm bos.perf.perfstat bos.perf.libperfstat bos.perf.proctools bos.adt.prof bos.cifs_fs.rte
  Fileset                      Level  State      Description        
  ----------------------------------------------------------------------------
Path: /usr/lib/objrepos
  bos.adt.base              5.3.0.60  COMMITTED  Base Application Development
                                                 Toolkit
  bos.adt.lib               5.3.0.60  COMMITTED  Base Application Development
                                                 Libraries
  bos.adt.libm              5.3.0.60  COMMITTED  Base Application Development
                                                 Math Library
  bos.adt.prof              5.3.0.60  COMMITTED  Base Profiling Support
  bos.cifs_fs.rte           5.3.0.60  COMMITTED  Runtime for SMBFS
  bos.perf.libperfstat      5.3.0.60  COMMITTED  Performance Statistics Library
                                                 Interface
  bos.perf.perfstat         5.3.0.60  COMMITTED  Performance Statistics
                                                 Interface
  bos.perf.proctools        5.3.0.60  COMMITTED  Proc Filesystem Tools

Path: /etc/objrepos
  bos.cifs_fs.rte           5.3.0.60  COMMITTED  Runtime for SMBFS
  bos.perf.libperfstat      5.3.0.60  COMMITTED  Performance Statistics Library
                                                 Interface
  bos.perf.perfstat         5.3.0.60  COMMITTED  Performance Statistics
                                                 Interface

对于缺少的包,用installp -a -d /dev/cd0 包名  进行安装,安装时放入AIX的安装光备                                    

检查安装的补丁包
# /usr/sbin/instfix -i -k "IY58143 IY59386 IY60930 IY66513 IY70159"
    All filesets for IY58143 were found.
    All filesets for IY59386 were found.
    All filesets for IY60930 were found.
    All filesets for IY66513 were found.
    All filesets for IY70159 were found.

# /usr/sbin/instfix -i -k "IY68989"
    All filesets for IY68989 were found.

安装AIX C++ 8.0 运行时

编辑/etc/hosts文件
   
创建用户组oinstall,dba
mkgroup -'A' oinstall
mkgroup -'A' dba
创建用户oracle
修改oracle用户密码
确认nobody用户存在 id nobdy
配置shell limits( smit chuser)
  soft FILE size -1
  soft CPU time -1
  soft DATA segment -1
  soft STACK size -1
更改系统参数 smit chgsys
  将 Maximum number of PROCESSES allowed per user改为2048以上
 
创建目录
# mkdir -p /u01/app/oracle
# chown oracle:oinstall /u01/app/oracle
# chmod 775 /u01/app/oracle
# mkdir -p /u02/oradata
# chown oracle:oinstall /u02/oradata
# chmod 755 /u02/oradata

创建文件系统/u01 /u02
   
切换到oracle用户,在.profile中增加如下两行
umask 022
AIXTHREAD_SCOPE=S; export AIXTHREAD_SCOPE
ORACLE_BASE=/u01/app/oracle; export ORACLE_BASE

执行
  . ./.profile
将安装文件ftp到主机
解压文件
gunzip 10gr2_aix5l64_database.cpio.gz
cpio -idmv  10gr2_aix5l64_database.cpio (如果报0511-903 Out of phase这样的错误,cpio命令加上c 这个参数,即idmcv)                                                  
以root用户执行 Disk1/rootpre/rootpre.sh
切换回oracle用户
export DISPLAY
运行Disk1/runInstaller完成安装

完成后注意修改操作系统maxperm% maxclient% minperm% v_pinshm strict_maxclient strict_maxperm参数

前一天遇到一例database link问题,仅供大家参考:

数据库是运行在Windows 2003上的Oracle 9i

先来看看故障现象,维护人员发现数据库上的job不能正常执行。用TOAD手工调用job的存储过程,TOAD一直阻塞。

经过这个故障的过程如下:

1.      在TOAD中手工执行存储过程,TOAD挂起。
2. 用sqlplus连接到数据库,检查v$session_wait,发现执行存储过程的会话正在等待library cache pin。
3. 通过x$kglob与v$session_wait,发现等待library cache pin的SQL语句为:
         select flag from account@zw.world ....
4. 手工执行此SQL,客户端挂起。怀疑是数据链路问题,执行select * from dual@zw.world,客户端也挂起。
5. 在sqlplus下,新建数据库zw_link,执行select * from dual@zw_link,执行此SQL仍然会导致客户端挂起。
6. 怀疑数据库服务器不能正常接连到ZW库。在数据库服务器上用sqlplus连接ZW库:
       sqlplus xxx/xxx@zw
    这时数据库服务器(windows系统)弹出一对话框,windows木马防火墙提示是否允许sqlplus程序访问zw的IP地址的信息。看来是由于木马防火墙引起的问题。
7. 查看系统中运行的进程,发现windows木马防火墙软件是通过线程注入到其他进程的方式,对进程的网络访问进行控制。在进程要访问网络时,弹出对话框选择是否允许访问网络,但ORACLE进程是以服务方式运行的,没有设置为与桌面交互,所以弹 出的对话框不能显示,所以一直处于等待状态。
8. 去掉windows木马防火墙的自动启动,重启服务器,删除windows木马防火墙,检查数据库链接已经正常。
9. 手工执行存储过程,存储过程正常执行。检查存储过程运行产生的日志,也显示正常。至此问题解决。

看来现在的防病毒,防木马的工具也是越来越“厉害”了。