关注我们获得更多精彩
作者 | 李翔宇,云和恩墨西区交付技术顾问,长期服务移动运营商行业客户,精通 oracle 性能优化,故障诊断,特殊恢复领域。
众所周知,truncate table 是一种快速清空表内数据的一种方式,与 delete 方式不同,truncate 只产生非常少的 redo 和 undo,就实现了清空表数据并降低表 HWM 的功能。本文主要围绕 truncate table 的恢复来展开。
重要:假如确实失误 truncate 了表,需要马上停应用,最好将表空间设置为 offline 或者 read only,避免数据被覆盖。
通过之前对 table full scan、segment header 和前面 truncate 原理的分析,tfs 不会读取 L1,L2 块,所以恢复的时候 L1,L2 块和具体存放数据的块都不用管,只需尝试通过修改段头块和基表信息来恢复 truncate 的数据,bbed 修改段头块的具体 offset 含义见 segment header 章节。
由于表可能不止一次被 truncate,所以获取之前 dataobj# 最好的办法是通过 logminer 或者 redo dump,这里我使用的 redo dump
REDO RECORD - Thread:1 RBA: 0x000055.0000001b.0080 LEN: 0x00ac VLD: 0x01
SCN: 0x0000.003ed056 SUBSCN: 1 04/21/2018 12:25:34
CHANGE #1 TYP:0 CLS:34 AFN:6 DBA:0x018002ee OBJ:4294967295 SCN:0x0000.003ed054 SEQ:1 OP:5.1 ENC:0 RBL:0
ktudb redo: siz: 64 spc: 822 flg: 0x0022 seq: 0x017b rec: 0x33
xid: 0x0009.00f.00000353
ktubu redo: slt: 15 rci: 50 opc: 14.5 objn: 1 objd: 16840 tsn: 5
Undo type: Regular undo Undo type: Last buffer split: No
Tablespace Undo: Yes
0x00000000
kteopu undo - undo operation on extent map
segdba: 0x140076a class: 4 mapdba:0x140076a offset: 2
rbr extent - dba: 0x0 nbk: 0x0
kteop redo - redo operation on extent map
CDOBJ: new object number:16840
CHANGE #2 TYP:0 CLS:4 AFN:5 DBA:0x0140076a OBJ:16840 SCN:0x0000.003ed050 SEQ:2 OP:14.4 ENC:0 RBL:0
kteop redo - redo operation on extent map
CDOBJ: new object number:16860
从 redo dump 信息可以看到段头块 0x0140076a 的 dataobj#从16840 变成了16860,所以这里需要将段头块的 dataobj# 改回 16840,通过 bbed 进行修改:
BBED> set file 4 block 1898
FILE# 4
BLOCK# 1898
BBED> d offset 272 count 8
File: /u01/app/oracle/oradata/test/users01.dbf (4)
Block: 1898 Offsets: 272 to 279 Dba:0x0100076a
------------------------------------------------------------------------
dc410000 00000010
<32 bytes per line>
BBED> m /x c841 offset 272
Warning: contents of previous BIFILE will be lost. Proceed? (Y/N) y
File: /u01/app/oracle/oradata/test/users01.dbf (4)
Block: 1898 Offsets: 272 to 279 Dba:0x0100076a
------------------------------------------------------------------------
c8410000 00000010
<32 bytes per line>
BBED> sum apply
Check value for File 4, Block 1898:
current = 0xe90e, required = 0xe90e
仅仅修改段头块的 dataobj# 再次查询表会报 ORA-08103: object no longer exists,原因是基表没有修改。查询的时候会通过表名去找到该表的 dataobj#
SYS@TEST(test):1>select count(*) from test.truncate_table;
select count(*) from test.truncate_table
*
ERROR at line 1:
ORA-08103: object no longer exists
SYS@TEST(test):1>UPDATE OBJ$ SET DATAOBJ#=16840 WHERE OBJ#=16840;
1 row updated.
SYS@TEST(test):1>UPDATE TAB$ SET DATAOBJ#=16840 WHERE OBJ#=16840;
1 row updated.
SYS@TEST(test):1>COMMIT;
Commit complete.
SYS@TEST(test):1>alter system flush buffer_cache;
System altered.
SYS@TEST(test):1>alter system flush shared_pool;
System altered.
SYS@TEST(test):1>select count(*) from test.truncate_table;
COUNT(*)
----------
0
HWM 信息可以从 redo dump 中获取
REDO RECORD - Thread:1 RBA: 0x000055.00000016.0148 LEN: 0x01b8 VLD: 0x01
SCN: 0x0000.003ed050 SUBSCN: 5 04/21/2018 12:25:34
CHANGE #1 TYP:0 CLS:34 AFN:6 DBA:0x018002ee OBJ:4294967295 SCN:0x0000.003ed050 SEQ:3 OP:5.1 ENC:0 RBL:0
ktudb redo: siz: 164 spc: 1112 flg: 0x0022 seq: 0x017b rec: 0x30
xid: 0x0009.00f.00000353
ktubu redo: slt: 15 rci: 47 opc: 13.29 objn: 16840 objd: 16840 tsn: 5
Undo type: Regular undo Undo type: Last buffer split: No
Tablespace Undo: No
0x00000000
Segment Header Undo
Seghdr dba: 0x0140076a Mapblock dba: 0x00000000 Mapredo Offset: 4 scls: 4 mcls: 140733193388039
Both the HWMs
Low HWM
Highwater:: 0x01405b83 ext#: 27 blk#: 3 ext size: 128
#blocks in seg. hdr's freelists: 0
#blocks below: 1539
mapblk 0x00000000 offset: 27
lfdba: 0x01405b80
High HWM
Highwater:: 0x01405b83 ext#: 27 blk#: 3 ext size: 128
#blocks in seg. hdr's freelists: 0
#blocks below: 1539
mapblk 0x00000000 offset: 27
lfdba: 0x01405b80 hint dba: 0x01400769
Lasts in Header
LF: 20994945 LS: 20973417 LT: 0 FT: 0
CHANGE #2 TYP:0 CLS:4 AFN:5 DBA:0x0140076a OBJ:16840 SCN:0x0000.003ed050 SEQ:1 OP:13.28 ENC:0 RBL:0
Both the HWMs
Low HWM
Highwater:: 0x0140076b ext#: 0 blk#: 3 ext size: 8
#blocks in seg. hdr's freelists: 0
#blocks below: 0
mapblk 0x00000000 offset: 0
lfdba: 0x01400768
High HWM
Highwater:: 0x0140076b ext#: 0 blk#: 3 ext size: 8
#blocks in seg. hdr's freelists: 0
#blocks below: 0
mapblk 0x00000000 offset: 0
lfdba: 0x01400768 hint dba: 0x01400769
Lasts in Header
LF: 20973416 LS: 20973417 LT: 0 FT: 0
CHANGE #3 TYP:0 CLS:8 AFN:5 DBA:0x01400768 OBJ:16840 SCN:0x0000.003ed050 SEQ:2 OP:13.22 ENC:0 RBL:0
Redo on Level1 Bitmap Block
Redo to set hwm
Opcode: 32 Highwater:: 0x0140076b ext#: 0 blk#: 3 ext size: 8
#blocks in seg. hdr's freelists: 0
#blocks below: 0
mapblk 0x00000000 offset: 0
可以看到段头块 0x0140076a 的 LHWM 信息和 HHWM 是一样的,都是从
Highwater:: 0x01405b83 ext#: 27 blk#: 3 ext size: 128
变成了
Highwater:: 0x0140076b ext#:0 blk#:3 extsize:8
所以这里恢复 HWM 信息只需要根据 redo dump 改回去即可
m /x 1b offset 48
m /x 1b offset 92
m /x 03 offset 52
m /x 03 offset 96
m /x 80 offset 56
m /x 80 offset 100
m /x 835b offset 60
m /x 4001 offset 62
m /x 835b offset 104
m /x 4001 offset 106
extent 信息和 aux map 信息同样可以从 redo dump 中获取
REDO RECORD - Thread:1 RBA: 0x000055.00000027.0180 LEN: 0x0138 VLD: 0x01
SCN: 0x0000.003ed061 SUBSCN: 3 04/21/2018 12:25:35
CHANGE #1 TYP:0 CLS:17 AFN:6 DBA:0x01800120 OBJ:4294967295 SCN:0x0000.003ed061 SEQ:1 OP:5.2 ENC:0 RBL:0
ktudh redo: slt: 0x0014 sqn: 0x00000000 flg: 0x0002 siz: 112 fbi: 248
uba: 0x018006ea.018c.1f pxid: 0x0000.000.00000000
CHANGE #2 TYP:0 CLS:18 AFN:6 DBA:0x018006ea OBJ:4294967295 SCN:0x0000.003ed061 SEQ:1 OP:5.1 ENC:0 RBL:0
ktudb redo: siz: 112 spc: 3358 flg: 0x0022 seq: 0x018c rec: 0x1f
xid: 0x0001.014.00000297
ktubu redo: slt: 20 rci: 0 opc: 14.5 objn: 1 objd: 16860 tsn: 5
Undo type: Regular undo Undo type: Last buffer split: No
Tablespace Undo: Yes
0x00000000
kteopu undo - undo operation on extent map
segdba: 0x140076a class: 4 mapdba:0x140076a offset: 3
rbr extent - dba: 0x0 nbk: 0x0
kteop redo - redo operation on extent map
ADD: dba:0x1405b80 len:128 at offset:27 --truncate前的ext#为27 的extent条目信息
ADDAXT: offset:27 fdba:x01405b80 bdba:0x01405b82 --truncate前ext#为27的aux map条目信息
SETSTAT: exts:28 blks:1664 lastmap:0x0 mapcnt:0
CHANGE #3 TYP:0 CLS:4 AFN:5 DBA:0x0140076a OBJ:16860 SCN:0x0000.003ed061 SEQ:2 OP:14.4 ENC:0 RBL:0
kteop redo - redo operation on extent map
DELETE: entry:27
shift back: dba:0x0 len:0
SETSTAT: exts:27 blks:1536 lastmap:0x0 mapcnt:0
从 redo dump 可以发现 truncate 操作对于 extent map 和 aux map 是从最后一个 extent 开始逐一删除的,这里可以看到该表的 extent 总共有 28 个,ext#为27 是该表最后一个 extent,block_id 为 0x1405b80,该 extent size 为 128 个块,以此类推很容易可以通过简单的 grep 找出 extent map;同理 ext#为27 的 aux map 的 L1 dba 为 x01405b80,data dba 为 0x01405b82,以此类推很容易可以通过简单的 grep 找出 aux map
extent map:
[root@prim1-11g ~]# grep -i "ADD: dba" /u01/app/oracle/diag/rdbms/test/test/trace/test_ora_7611.trc
ADD: dba:0x1405b80 len:128 at offset:27
ADD: dba:0x1405b00 len:128 at offset:26
ADD: dba:0x1405a80 len:128 at offset:25
ADD: dba:0x1405a00 len:128 at offset:24
ADD: dba:0x1405980 len:128 at offset:23
ADD: dba:0x1405900 len:128 at offset:22
ADD: dba:0x1405880 len:128 at offset:21
ADD: dba:0x1405800 len:128 at offset:20
ADD: dba:0x1404180 len:128 at offset:19
ADD: dba:0x1404080 len:128 at offset:18
ADD: dba:0x1404000 len:128 at offset:17
ADD: dba:0x1403f80 len:128 at offset:16
ADD: dba:0x1402eb8 len:8 at offset:15
ADD: dba:0x1402eb0 len:8 at offset:14
ADD: dba:0x1402ea8 len:8 at offset:13
ADD: dba:0x1402ea0 len:8 at offset:12
ADD: dba:0x1402e98 len:8 at offset:11
ADD: dba:0x1402e90 len:8 at offset:10
ADD: dba:0x1402e88 len:8 at offset:9
ADD: dba:0x1402e80 len:8 at offset:8
ADD: dba:0x1402f78 len:8 at offset:7
ADD: dba:0x1402f70 len:8 at offset:6
ADD: dba:0x1402f68 len:8 at offset:5
ADD: dba:0x1402f60 len:8 at offset:4
ADD: dba:0x1402f58 len:8 at offset:3
ADD: dba:0x1402f50 len:8 at offset:2
ADD: dba:0x1402f48 len:8 at offset:1
aux map:
[root@prim1-11g ~]# grep -i "ADDAXT:" /u01/app/oracle/diag/rdbms/test/test/trace/test_ora_7611.trc
ADDAXT: offset:27 fdba:x01405b80 bdba:0x01405b82
ADDAXT: offset:26 fdba:x01405b00 bdba:0x01405b02
ADDAXT: offset:25 fdba:x01405a80 bdba:0x01405a82
ADDAXT: offset:24 fdba:x01405a00 bdba:0x01405a02
ADDAXT: offset:23 fdba:x01405980 bdba:0x01405982
ADDAXT: offset:22 fdba:x01405900 bdba:0x01405902
ADDAXT: offset:21 fdba:x01405880 bdba:0x01405882
ADDAXT: offset:20 fdba:x01405800 bdba:0x01405802
ADDAXT: offset:19 fdba:x01404180 bdba:0x01404182
ADDAXT: offset:18 fdba:x01404080 bdba:0x01404082
ADDAXT: offset:17 fdba:x01404000 bdba:0x01404002
ADDAXT: offset:16 fdba:x01403f80 bdba:0x01403f82
ADDAXT: offset:15 fdba:x01402eb0 bdba:0x01402eb8
ADDAXT: offset:14 fdba:x01402eb0 bdba:0x01402eb1
ADDAXT: offset:13 fdba:x01402ea0 bdba:0x01402ea8
ADDAXT: offset:12 fdba:x01402ea0 bdba:0x01402ea1
ADDAXT: offset:11 fdba:x01402e90 bdba:0x01402e98
ADDAXT: offset:10 fdba:x01402e90 bdba:0x01402e91
ADDAXT: offset:9 fdba:x01402e80 bdba:0x01402e88
ADDAXT: offset:8 fdba:x01402e80 bdba:0x01402e81
ADDAXT: offset:7 fdba:x01402f70 bdba:0x01402f78
ADDAXT: offset:6 fdba:x01402f70 bdba:0x01402f71
ADDAXT: offset:5 fdba:x01402f60 bdba:0x01402f68
ADDAXT: offset:4 fdba:x01402f60 bdba:0x01402f61
ADDAXT: offset:3 fdba:x01402f50 bdba:0x01402f58
ADDAXT: offset:2 fdba:x01402f50 bdba:0x01402f51
ADDAXT: offset:1 fdba:x01400768 bdba:0x01402f48
这里可以写脚本根据 segment header 章节 offset 的含义来生成 bbed 命令,由于 bbed 命令较长省略一部分
修改exts信息:
m /x 1c offset 36
m /x 1c offset 264
将 grep 出的数据作为一整列全部导入表中用 sql 生成 bbed 命令
如创建表:create table aux_map(a varchar2(2000);create table ext_map(a varchar2(2000);插入数据后就可以用下列 sql 生成命令
生成 aux map 的 bbed 命令
with aa as (
select replace(regexp_substr(a,'[^:]+',1,3),'fdba','') ext#,
(replace(regexp_substr(a,'[^:]+',1,3),'fdba','')-1)*8+2744 offset,
trim(replace(regexp_substr(a,'[^:]+',1,4),'bdba','')) l1,
trim(regexp_substr(a,'[^:]+',1,5)) data
from aux_map)
select 'm /x '||a||' offset '||offset||chr(10)||'m /x '||b||' offset '||to_char(offset+2)||chr(10)||'m /x '||c||' offset '||to_char(offset+4)||chr(10)||'m /x '||d||' offset '||to_char(offset+6) from
(select ext#,to_number(offset) offset,substr(l1,-2,2)||substr(l1,-4,2) a,substr(l1,-6,2)||substr(l1,-8,2) b,substr(data,-2,2)||substr(data,-4,2) c,substr(data,-6,2)||substr(data,-8,2) d
from aa)
生成 ext map 的 bbed 命令
with aa as (
select trim(replace(replace(regexp_substr(a,'[^:]+',1,3),'len',''),'x','x0')) block_id,
(regexp_substr(a,'[^:]+',1,5)-1)*8+288 offset,
lpad(trim((to_char((replace(regexp_substr(a,'[^:]+',1,4),'at offset','')),'xxxx'))),2,0) blocks,
regexp_substr(a,'[^:]+',1,5) ext#
from ext_map)
select 'm /x '||a||' offset '||offset||chr(10)||'m /x '||b||' offset '||to_char(offset+2)||chr(10)||'m /x '||blocks||' offset '||to_char(offset+4) from
(select ext#,to_number(offset) offset,substr(block_id,-2,2)||substr(block_id,-4,2) a,substr(block_id,-6,2)||substr(block_id,-8,2) b,blocks
from aa)
bbed 修改后 extent map 恢复成功:
BBED> d /v offset 280
File: /u01/app/oracle/oradata/test/users01.dbf (4)
Block: 1898 Offsets: 280 to 579 Dba:0x0100076a
-------------------------------------------------------
68074001 08000000 482f4001 08000000 l [email protected]/@.....
502f4001 08000000 582f4001 08000000 l P/@.....X/@.....
602f4001 08000000 682f4001 08000000 l `/@.....h/@.....
702f4001 08000000 782f4001 08000000 l p/@.....x/@.....
802e4001 08000000 882e4001 08000000 l ..@.......@.....
902e4001 08000000 982e4001 08000000 l ..@.......@.....
a02e4001 08000000 a82e4001 08000000 l ..@.......@.....
b02e4001 08000000 b82e4001 08000000 l ..@.......@.....
803f4001 80000000 00404001 80000000 l .?@......@@.....
80404001 80000000 80414001 80000000 l .@@......A@.....
00584001 80000000 80584001 80000000 l [email protected]@.....
00594001 80000000 80594001 80000000 l [email protected]@.....
005a4001 80000000 805a4001 80000000 l [email protected]@.....
005b4001 80000000 805b4001 80000000 l .[@......[@.....
00000000 00000000 00000000 00000000 l ................
00000000 00000000 00000000 00000000 l ................
00000000 00000000 00000000 00000000 l ................
00000000 00000000 00000000 00000000 l ................
00000000 00000000 00000000 l ............
<16 bytes per line>
aux map 恢复成功:
BBED> d /v offset 2736
File: /u01/app/oracle/oradata/test/users01.dbf (4)
Block: 1898 Offsets: 2736 to 3035 Dba:0x0100076a
-------------------------------------------------------
68074001 6b074001 68074001 482f4001 l [email protected][email protected][email protected]/@.
502f4001 512f4001 502f4001 582f4001 l P/@.Q/@.P/@.X/@.
602f4001 612f4001 602f4001 682f4001 l `/@.a/@.`/@.h/@.
702f4001 712f4001 702f4001 782f4001 l p/@.q/@.p/@.x/@.
802e4001 812e4001 802e4001 882e4001 l ..@...@...@...@.
902e4001 912e4001 902e4001 982e4001 l ..@...@...@...@.
a02e4001 a12e4001 a02e4001 a82e4001 l ..@...@...@...@.
b02e4001 b12e4001 b02e4001 b82e4001 l ..@...@...@...@.
803f4001 823f4001 00404001 02404001 l .?@..?@..@@..@@.
80404001 82404001 80414001 82414001 l .@@..@@[email protected]@.
00584001 02584001 80584001 82584001 l [email protected]@[email protected]@.
00594001 02594001 80594001 82594001 l [email protected]@[email protected]@.
005a4001 025a4001 805a4001 825a4001 l [email protected]@[email protected]@.
005b4001 025b4001 805b4001 825b4001 l .[@..[@..[@..[@.
00000000 00000000 00000000 00000000 l ................
00000000 00000000 00000000 00000000 l ................
00000000 00000000 00000000 00000000 l ................
00000000 00000000 00000000 00000000 l ................
00000000 00000000 00000000 l ............
<16 bytes per line>
最后恢复成功
这里由于L1,L2没有恢复,所以insert会有问题,但是可以通过CTAS重建表完全恢复。
SYS@TEST(test):1>select count(*) from test.truncate_table;
COUNT(*)
----------
113426
SYS@TEST(test):1>insert into test.truncate_table select * from dba_objects;
insert into test.truncate_table select * from dba_objects
*
ERROR at line 1:
ORA-00600: internal error code, arguments: [ktspgfblk3:kcbz_objdchk], [0], [0], [1], [], [], [], [], [], [], [], []
SYS@TEST(test):1>CREATE TABLE TEST.RECOVER_TABLE AS SELECT * FROM TEST.TRUNCATE_TABLE;
Table created.
SYS@TEST(test):1>INSERT INTO TEST.RECOVER_TABLE select * from dba_objects WHERE ROWNUM=1;
1 row created.
SYS@TEST(test):1>COMMIT;
Commit complete.
但是这种恢复方式只适用于truncate的数据没有被覆盖,也就是没有其他对象占用了truncate所释放的空间,所以在操作时需要查询dba_extents对比redo dump出来的extent map,如果没有覆盖的情况,则上述方法可行。那么被覆盖的情况该如何处理呢?
建立测试环境:
创建测试表 truncate_table_2,truncate 后创建 aaa 表覆盖 truncate_table_2 表 truncate 后释放的空间。
SYS@TEST(test):1>create table test.truncate_table_2 tablespace test as select * from dba_objects where rownum<=6000;
Table created.
SYS@TEST(test):1>select extent_id,file_id,block_id,blocks from dba_extents where segment_name='TRUNCATE_TABLE_2';
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
---------- ---------- ---------- ----------
0 4 136 8
1 4 144 8
2 4 152 8
3 4 160 8
4 4 168 8
5 4 176 8
6 4 184 8
7 4 192 8
8 4 200 8
9 4 208 8
10 4 216 8
11 rows selected.
SYS@TEST(test):1>select count(*) from test.truncate_table_2;
COUNT(*)
----------
6000
SYS@TEST(test):1>truncate table test.truncate_table_2;
Table truncated.
SYS@TEST(test):1>alter system switch logfile;
System altered.
SYS@TEST(test):1>oradebug setmypid;
Statement processed.
SYS@TEST(test):1>alter system dump logfile '/u01/app/oracle/oradata/test/redo01.log';
System altered.
SYS@TEST(test):1>oradebug tracefile_name
/u01/app/oracle/diag/rdbms/test/test/trace/test_ora_17034.trc
SYS@TEST(test):1>create table test.aaa tablespace test as select * from dba_objects where rownum<=500;
Table created.
SYS@TEST(test):1>select extent_id,file_id,block_id,blocks from dba_extents where segment_name='AAA';
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
---------- ---------- ---------- ----------
0 4 144 8
1 4 152 8
在这里就可以清楚的发现表 truncate_table_2 的数据被表 aaa 覆盖了一部分。
move 之前先查询该对象占用的 extent 信息,并且 dump segment header 以便后续的处理
SYS@:>select extent_id,file_id,block_id,blocks from dba_extents where segment_name='AAA';
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
---------- ---------- ---------- ----------
0 4 144 8
1 4 152 8
SYS@:>alter table test.aaa move tablespace users;
Table altered.
这里步骤和没有覆盖情况一致,不做详述。
BBED> m /x 3e offset 272 --修改段头dataobj#
/*修改数据字典信息
SYS@TEST(test):1>update obj$ set dataobj#=16958 where obj#=16958;
1 row updated.
SYS@TEST(test):1>update tab$ set dataobj#=16958 where obj#=16958;
1 row updated.
SYS@TEST(test):1>commit;
Commit complete.
*/
/*从redo dump里获取HWM信息并修改段头块
m /x 0a offset 48
m /x 0a offset 92
m /x 05 offset 52
m /x 05 offset 96
m /x 08 offset 56
m /x 08 offset 100
m /x dd00 offset 60
m /x 0001 offset 62
m /x dd00 offset 104
m /x 0001 offset 106
*/
/*修改段头ext map、aux map以及extent个数
略
*/
尝试查询报错
SYS@TEST(test):1>select count(*) from test.truncate_table_2;
select count(*) from test.truncate_table_2
*
ERROR at line 1:
ORA-08103: object no longer exists
剩下的问题,其实就是解决 8103 的问题了,实际上 8103 的错误原因就是被覆盖的数据块的 dataobj# 和 truncate_table_2 的 dataobj# 不一致导致的
Corrupt block relative dba: 0x01000090 (file 4, block 144)
Fractured block found during multiblock buffer read
Data in bad block:
type: 6 format: 2 rdba: 0x01000090
last change scn: 0x0000.003f9e3d seq: 0x3 flg: 0x04
spare1: 0x0 spare2: 0x0 spare3: 0x0
consistency value in tail: 0x9e3d2003
check value in block header: 0x5bde
computed block checksum: 0x0
Reading datafile '/u01/app/oracle/oradata/test/TEST/datafile/o1_mf_test_fg6gjt1d_.dbf' for corruption at rdba: 0x01000090 (file 4, block 144)
Reread (file 4, block 144) found same corrupt data (no logical check)
*** SESSION ID:(178.85) 2018-04-28 00:24:12.968
OBJD MISMATCH typ=33, seg.obj=16958, diskobj=16960, dsflg=100001, dsobj=16958, tid=16958, cls=1
明确指出在全表扫描的时候发现file 4 block 144的dataobj#为16960,但是seg的dataobj#为16958。
表AAA在move之前,需要先查出extent信息和dump segment header,目的就在这里(假如没有dump 段头仍然可以从redo dump里找到HWM信息)
extent 信息
SYS@:>select extent_id,file_id,block_id,blocks from dba_extents where segment_name='AAA';
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
---------- ---------- ---------- ----------
0 4 144 8
1 4 152 8
可以确定4号文件144号块开始被覆盖,但是不一定ext#为1的最后一个块也被覆盖了,因为LHWM和HHWM之间的块不一定被覆盖
HWM信息
Highwater:: 0x0100009a ext#: 1 blk#: 2 ext size: 8 --file 4 block 154
构造 rowid 绕过 dataobj# 不匹配的块
SYS@TEST(test):1>select count(*) from
2 (select * from test.truncate_table_2 where rowid
3 union all
4 select * from test.truncate_table_2 where rowid>=dbms_rowid.rowid_create(1, 16958, 4, 154, 0));
COUNT(*)
----------
5303
成功挽救 5303 条数据,少了 697 条,也可以用 dbms_repair 标记这些块为坏块,然后跳过这些块。
对于 truncate 的恢复,在无备份的情况下,如果需要恢复的表比较多,或者分区表的分区比较多,修复元数据的方式较为麻烦,还是建议使用 odu 来进行恢复。
作者:李翔宇
投稿:有投稿意向技术人请在公众号对话框留言。
转载:意向文章下方留言。
更多精彩请关注 “数据和云” 公众号
资源下载
关注公众号:数据和云(OraNews)回复关键字获取
2018DTCC , 数据库大会PPT
2017DTC,2017 DTC 大会 PPT
DBALIFE ,“DBA 的一天”海报
DBA04 ,DBA 手记4 电子书
122ARCH ,Oracle 12.2体系结构图
2017OOW ,Oracle OpenWorld 资料
PRELECTION ,大讲堂讲师课程资料
近期文章仅仅使用AWR做报告? 性能优化还未入门
实战课堂:一则CPU 100%的故障分析
杨廷琨:如何编写高效SQL(含PPT)
一份高达555页的技术PPT会是什么样子?
大象起舞:用PostgreSQL解海盗分金问题
ProxySQL!像C罗一样的强大
高手过招:用SQL解决环环相扣刑侦推理问题