使用dbms_rowid抢救数据


通过DBMS_REPAIR包或设置10231事件,抢救坏块之外数据的方法,相比即将介绍的ROWID扫描方法,更为简洁。但上述方法只适合于出现ORA-1578的情况,而且根据经验,该方法经常不能有效进行数据抢救。因此,下面我们将详细介绍ROWID扫描方法。

测试环境准备

原理

rowid

在这里插入图片描述
在这里插入图片描述
在oracle数据库中,rowid可以定位表某一具体的行位置。所以其实使用rowid进行访问是最快的. rowid其实在oracle 8i之前就有了,随着数据量的增大,从oracle 8i开始对rowid进行了扩展,被称为extented rowid。

  1. 64位ascii编码

8i之前的rowid长度是6位,而前面8i之后看到的extented rowid都是10位. 由64位的18个ascii字符组成. 分别对应了文件号,block号,row位置等. 如下是个具体的例子:
在这里插入图片描述
将base64编码转换为十进制:
计算data_object_id:
第1位:A,那么则为 0*(64^5)= 0
第2位:A,那么则为 0*(64^4)= 0
第3位:A,那么则为 0*(64^3)= 0
第4位:T,那么则为 19*(64^2)= 77824
第5位:r,那么则为 43*(64^1)= 2752
第6位:x,那么则为 46*(64^0)= 46
0+0+0+77824+2752+46=80622 即是我们的data_object_id值. 我们来验证一下:

SQL> select data_object_id,object_id from dba_objects where object_name='ZHUO';

DATA_OBJECT_ID  OBJECT_ID
-------------- ----------
         80622      8062
  1. rowid dump结果
    下面我们再来看下rowid的长度:
SQL> select dump(rowid,16) from zhuo where object_id=2;

DUMP(ROWID,16)
--------------------------------------------------------------------------------
Typ=69 Len=10: 0,1,3a,ee,1,40,5,83,0,30

从dump我们可以发现rowid 占据10个byte. 由于一个byte 等于8个bit. 前面4个bytes就表示object_id号,如下转换得知:
0,1,3a,ee,1,40,5,83,0,30
0000 0000 0000 0001 0011 1010 1110 1110 0000 0001 0100 0000 0000 0101 1000 0011 0000 0000 0011 0000

SQL> SQL> select to_number('13aee','xxxxxxx') from dual;

TO_NUMBER('13AEE','XXXXXXX')
----------------------------
                       80622

剩余的字节可以按如上方法进行转化。

简单的总结如下:

rowid一共80个bit长度,其中:

1~32 bit,共32个bit表示object_id号.
33~42 bit,共10个bit表示file id
43~64 bir,共22个bit表示block number.
65~80 bit,共聚16个bit表示row 号.

0,1,3a,ee,1,40,5,83,0,30在这里插入图片描述
0000 0000 0000 0001 0011 1010 1110 1110 0000 00001 0100 0000 0000 0101 1000 0011 0000 0000 0011 0000
0000 0001 01=5 ;
00 0000 0000 0101 1000 0011=1411;
0000 0000 0011 0000=48
验证:

 SQL> select dbms_rowid.rowid_object(rowid) obj#,
  2  dbms_rowid.rowid_relative_fno(rowid) rfile#,
  3  dbms_rowid.rowid_block_number(rowid) block#,
  4  dbms_rowid.rowid_row_number(rowid) row#
  5  from zhuo.zhuo where rowid='AAATruAAFAAAAWDAAw';

      OBJ#     RFILE#     BLOCK#       ROW#
---------- ---------- ---------- ----------
     80622          5       1411         48

从上可知,select查询出来的rowid格式为:AAATruAAFAAAAWDAAw
而dump出来的rowid格式为:Len=10: 0,1,3a,ee,1,40,5,83,0,30
select查询出来的,由64位的18个ascii字符组成.分别对应了文件号,block号,row位置等。
dump出来的,为80bit,真实存储的内容。
两者只是rowid不同的表现形式而已,都可以进行相应的转化。

通过如下函数,可创建一条记录的ROWID
function ROWID_CREATE(rowid_type IN number,
object_number IN number,
relative_fno IN number,
block_number IN number,
row_number IN number)
return ROWID;

— rowid_type – type (restricted=0/extended=1)
— object_number – data object number
— relative_fno – relative file number
— block_number – block number in this file
— row_number – row number in this block

dbms_rowid

以下就是该技术流程图和示意图:在这里插入图片描述
即在定位坏块之后,通过DBMS_ROWID包去生成坏块所处的最小ROWID(LOW_RID),以及最大ROWID(HIGH_RID),例如:
— 最小ROWID
SELECT dbms_rowid.rowid_create(1,<DATA_OBJECT_ID>,,,0) LOW_RID from DUAL;

— 最大ROWID
SELECT dbms_rowid.rowid_create(1,<DATA_OBJECT_ID>,,+1,0) HI_RID from DUAL;
针对普通表,通过如下命令抢救数据:
CREATE TABLE salvage_table AS SELECT /*+ ROWID(A) / * FROM <owner.tablename> A WHERE rowid < ‘<low_rid>’ ;
INSERT INTO salvage_table SELECT /
+ ROWID(A) / * FROM <owner.tablename> A WHERE rowid >= ‘<high_rid>’ ;
针对分区表,通过如下命令抢救数据:
CREATE TABLE salvage_table AS
SELECT /
+ ROWID(A) / *
FROM <owner.tablename> PARTITION (<partition_name>) A
WHERE rowid < ‘<lo_rid>’;
INSERT INTO salvage_table
SELECT /
+ ROWID(A) */ *
FROM <owner.tablename> PARTITION (<partition_name>) A
WHERE rowid >= ‘<hi_rid>’
但是,如果坏块处于表段头(Segment Header),ROWID扫描法则无用武之地了。通过如下语句,可知道坏块是否处于表段头:
select file_id,block_id,blocks,extent_id
from dba_extents
where owner=’’
and segment_name=’<table_name>’
and segment_type=‘TABLE’
order by extent_id;

FILE_ID BLOCK_ID BLOCKS EXTENT_ID
——— ——— ——— ———
8 94854 20780 0 <- EXTENT_ID ZERO is segment header
即上述语句中,如果EXTEND_ID为0,则表示是表段头

使用rowid来抢救数据(实际也是跳过坏块)

SQL> select count(*) from zhuo.zhuo;

  COUNT(*)
----------
     80066

SQL> select count(*) from zhuo.zhuo where dbms_rowid.rowid_block_number(rowid)=1411;

  COUNT(*)
----------
        88

上面可知,表zhuo含有80066条记录,而block 1411里面包含88条记录。下面开始,
首先我们将上面的zhuo表的1411 block处理为坏块,步骤略.

SQL> select count(*) from zhuo.zhuo;
select count(*) from zhuo.zhuo
                          *
ERROR at line 1:
ORA-01578: ORACLE data block corrupted (file # 5, block # 1411)
ORA-01110: data file 5:
'/u01/app/oracle/oradata/ZHUO/datafile/o1_mf_zhuo_gxdcfr5s_.dbf'

查询所需rowid

SQL>  select dbms_rowid.rowid_create(1,80622,5,1411,0) from dual;

DBMS_ROWID.ROWID_C
------------------
AAATruAAFAAAAWDAAA

SQL>  select dbms_rowid.rowid_create(1,80622,5,1412,0) from dual;

DBMS_ROWID.ROWID_C
------------------
AAATruAAFAAAAWEAAA

跳过坏块,抢救数据:

SQL> CREATE TABLE salvage_table AS SELECT /*+ ROWID(A) */ * FROM zhuo.zhuo A WHERE rowid < 'AAATruAAFAAAAWDAAA' ;


Table created.

SQL> INSERT INTO salvage_table SELECT /*+ ROWID(A) */ * FROM zhuo.zhuo A WHERE rowid >= 'AAATruAAFAAAAWEAAA';

79978 rows created.

SQL> commit ;

Commit complete.

SQL> select count(*) from salvage_table;

  COUNT(*)
----------
     79978

event 10231跳过原理

SQL> alter session SET EVENTS '10231 trace name context forever,level 10';


Session altered.

SQL> SQL> select count(*) from zhuo.zhuo;

  COUNT(*)
----------
     79978

SQL> create table test as select * from zhuo.zhuo;

Table created.

SQL> select count(*) from test;

  COUNT(*)
----------
     79978

我们可以通过设置10046 event来观察10231 event的实质:

SQL> ALTER SESSION SET EVENTS '10046 trace name context forever, level 12';

Session altered.

SQL> oradebug setmypid
Statement processed.
SQL> select count(*) from zhuo.zhuo;

  COUNT(*)
----------
     79978

SQL> oradebug tracefile_name;
/u01/app/oracle/diag/rdbms/zhuo/zhuo/trace/zhuo_ora_3059.trc
SQL> oradebug close_trace 
Statement processed.

trace部分信息:

*** 2020-12-05 04:07:59.965
WAIT #0: nam='SQL*Net message from client' ela= 9191565 driver id=1650815232 #bytes=1 p3=0 obj#=80632 tim=1607112479965917
=====================
PARSING IN CURSOR #140737300309752 len=30 dep=0 uid=0 oct=3 lid=0 tim=1607112479997716 hv=1309886796 ad='63739958' sqlid='0bnkcw1716kac'
select count(*) from zhuo.zhuo
END OF STMT
PARSE #140737300309752:c=7014,e=31709,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,plh=1214160148,tim=1607112479997715
EXEC #140737300309752:c=481,e=481,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=1214160148,tim=1607112479999641
WAIT #140737300309752: nam='SQL*Net message to client' ela= 1 driver id=1650815232 #bytes=1 p3=0 obj#=80632 tim=1607112479999974
table scan: segment: file# 5 block# 1410
            skipping corrupt block file# 5 block# 1411
FETCH #140737300309752:c=15998,e=27724,p=0,cr=1150,cu=0,mis=0,r=1,dep=0,og=1,plh=1214160148,tim=1607112480028162
STAT #140737300309752 id=1 cnt=1 pid=0 pos=1 obj=0 op='SORT AGGREGATE (cr=1150 pr=0 pw=0 time=27586 us)'
STAT #140737300309752 id=2 cnt=79978 pid=1 pos=1 obj=80622 op='TABLE ACCESS FULL ZHUO (cr=1150 pr=0 pw=0 time=10988 us cost=320 size=0 card=80066)'
WAIT #140737300309752: nam='SQL*Net message from client' ela= 163 driver id=1650815232 #bytes=1 p3=0 obj#=80632 tim=1607112480029411
FETCH #140737300309752:c=1,e=1,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=0,plh=1214160148,tim=1607112480029461
WAIT #140737300309752: nam='SQL*Net message to client' ela= 1 driver id=1650815232 #bytes=1 p3=0 obj#=80632 tim=1607112480029470

重要信息:
table scan: segment: file# 5 block# 1410
skipping corrupt block file# 5 block# 1411

SQL> select header_file,header_block from dba_segments where segment_name='ZHUO';

HEADER_FILE HEADER_BLOCK
----------- ------------
          5         1410

段头块位1410 block。
从上面的trace内容,我们可以发现,oracle在扫描段头后就跳过了标记的坏块,直接读取后面正常block中的数据被返回.
从这点也不难得出,dbms_repair和event 10231的实质是标记坏块,而标记的信息可能就存在段头block中.

段头损坏不能使用dbms_rowid抢救数据

原因:由文章各种坏块对表select/dml操作影响
测试可知,当PAGETABLE SEGMENT HEADER异常的时候,数据库包括select,update,delete,insert操作都不能进行。

SQL>  alter session SET EVENTS '10231 trace name context forever,level 10';

Session altered.

SQL> select  count(*) from zhuo.zhuo;
select  count(*) from zhuo.zhuo
                           *
ERROR at line 1:
ORA-01578: ORACLE data block corrupted (file # 5, block # 1410)
ORA-01110: data file 5:
'/u01/app/oracle/oradata/ZHUO/datafile/o1_mf_zhuo_gxdcfr5s_.dbf'

即时设置event,也不能跳过。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值