深入解析TRUNCATE TABLE – 手工修复和验证过程

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 众所周知,truncate table 是一种快速清空表内数据的一种方式,与 delete 方式不同,truncate 只产生非常少的 redo 和 undo,就实现了清空表数据并降低表 HWM 的功能。本文主要围绕 truncate table 的恢复来展开。

众所周知,truncate table 是一种快速清空表内数据的一种方式,与 delete 方式不同,truncate 只产生非常少的 redo 和 undo,就实现了清空表数据并降低表 HWM 的功能。本文主要围绕 truncate table 的恢复来展开。

truncate 的恢复
无覆盖的 TRUNCATE 恢复
重要:假如确实失误 truncate 了表,需要马上停应用,最好将表空间设置为 offline 或者 read only,避免数据被覆盖。

通过之前对 table full scan、segment header 和前面 truncate 原理的分析,tfs 不会读取 L1,L2 块,所以恢复的时候 L1,L2 块和具体存放数据的块都不用管,只需尝试通过修改段头块和基表信息来恢复 truncate 的数据,bbed 修改段头块的具体 offset 含义见 segment header 章节。

修改段头块 dataobj#
由于表可能不止一次被 truncate,所以获取之前 dataobj# 最好的办法是通过 logminer 或者 redo dump,这里我使用的 redo dump
REDO RECORD - Thread:1 RBA: 0x000055.0000001b.0080 LEN: `js
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
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 map,Auxillary Map 以及 extent 个数
extent 信息和 aux map 信息同样可以从 redo dump 中获取
REDO RECORD - Thread:1 RBA: 0x000055.00000027.0180 LEN: ```js
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+',1,3),'fdba','') ext#,
(replace(regexp_substr(a,'1+',1,3),'fdba','')-1)*8+2744 offset,
trim(replace(regexp_substr(a,'1+',1,4),'bdba','')) l1,
trim(regexp_substr(a,'1+',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+',1,3),'len',''),'x','x0')) block_id,
(regexp_substr(a,'1+',1,5)-1)*8+288 offset,
lpad(trim((to_char((replace(regexp_substr(a,'1+',1,4),'at offset','')),'xxxx'))),2,0) blocks,
regexp_substr(a,'1+',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 恢复成功:
B```js
BED> 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 h.@.....H/@.....
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 .X@......X@.....
00594001 80000000 80594001 80000000 l .Y@......Y@.....
005a4001 80000000 805a4001 80000000 l .Z@......Z@.....
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 h.@.k.@.h.@.H/@.
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 .@@..@@..A@..A@.
00584001 02584001 80584001 82584001 l .X@..X@..X@..X@.
00594001 02594001 80594001 82594001 l .Y@..Y@..Y@..Y@.
005a4001 025a4001 805a4001 825a4001 l .Z@..Z@..Z@..Z@.
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 恢复
建立测试环境:
创建测试表 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 覆盖了一部分。

将占用truncate释放的空间的对象move到其他表空间
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

解决 ORA-8103
构造 rowid 绕过 dataobj# 不匹配的块

SYS@TEST(test):1>select count(*) from
2  (select * from test.truncate_table_2 where rowid<dbms_rowid.rowid_create(1, 16958, 4, 144, 0)
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 来进行恢复。

原文发布时间为:2018-08-03
本文作者:李翔宇
本文来自云栖社区合作伙伴“数据和云”,了解相关信息可以关注“数据和云”。


  1. :
相关文章
|
22天前
|
域名解析 网络协议
邮箱域名解析后收不到短信?三步修复教程
邮箱域名解析后收不到短信?三步修复教程
|
5月前
|
存储 安全 Java
提升编程效率的利器: 解析Google Guava库之集合篇Table二维映射(四)
提升编程效率的利器: 解析Google Guava库之集合篇Table二维映射(四)
|
6月前
|
算法 数据安全/隐私保护
客户端验证 证书解析
客户端验证 证书解析
82 7
|
6月前
|
运维 网络协议 安全
Serverless 应用引擎产品使用之阿里云函数计算中添加自定义域名进行域名DNS验证如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
6月前
|
分布式计算 DataWorks 调度
DataWorks操作报错合集之DataWorks配置参数在开发环境进行调度,参数解析不出来,收到了 "Table does not exist" 的错误,该怎么处理
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
84 0
|
6月前
|
缓存 网络协议 安全
dns被劫持怎么修复?6种常用修复方法
dns被劫持怎么修复?6种常用修复方法
|
6月前
|
存储 安全 Java
Trembling ! Java类的加载过程详解(加载验证准备解析初始化使用卸载)
Trembling ! Java类的加载过程详解(加载验证准备解析初始化使用卸载)
65 0
|
缓存 算法 安全
转:深度解析如何利用递归算法来验证内网管理软件中的重要数据的完整性
用递归算法来验证内网管理软件里关键数据,就好比是在用放大镜审视一份份重要合同,确保它们都符合约定的条款,这样一来数据的品质和安全性都能得到提升。这种举重若轻的步骤,能够保证数据真实可靠,避免任何潜在的错误和漏洞。
75 1
|
数据采集 前端开发 JavaScript
解析网页弹窗验证机制及应对策略的Selenium爬虫案例
解析网页弹窗验证机制及应对策略的Selenium爬虫案例
|
JSON JavaScript 前端开发
一次采集JSON解析错误的修复
一次采集JSON解析错误的修复

推荐镜像

更多