这是在一次恢复演习中偶然发现的一个有趣的问题。
当LMT表空间中文件被offline后,如果该文件中包含有segment header,那么从dba_extents中不能看到该segment。
来看一个测试:
SQL> select file_id from dba_data_files where tablespace_name=’TEST';
FILE_ID
———-
10
11SQL> create table eagle_fan(x int) tablespace test;
Table created.
SQL> select header_file from dba_segments where segment_name=’EAGLE_FAN’ and owner=’SYS';
HEADER_FILE
———–
10SQL> select extent_id ,file_id from dba_extents where segment_name=’EAGLE_FAN’ and owner=’SYS';
EXTENT_ID FILE_ID
———- ———-
0 10SQL> select distinct segment_name from dba_extents where file_id=10;
SEGMENT_NAME
——————————————————————————–
TEST
EAGLE_FANSQL> alter database datafile 10 offline ;
Database altered.
SQL> select extent_id ,file_id from dba_extents where segment_name=’EAGLE_FAN’ and owner=’SYS';
SQL> select distinct segment_name from dba_extents where file_id=10;
SEGMENT_NAME
——————————————————————————–
TESTno rows selected
可以看到datafile 10被offline后,从dba_extents已经看不到该segment。推想而知,对于LMT tablespace,dba_extents会读取文件头信息。
那么也就是说,如果有文件被offline后,我们无法单从dba_extents中知道该文件中包含有哪些segment。
那么我们应该再从dba_segments中得到segment header在该文件中的segment:
SQL> select distinct segment_name from dba_extents where file_id=10
2 union
3 select distinct segment_name from dba_segments where header_file=10;SEGMENT_NAME
——————————————————————————–
EAGLE_FAN
TEST
而DMT tablespace不存在这个问题,我们来看一看dba_extents的视图定义就很清楚了:
select ds.owner, ds.segment_name, ds.partition_name, ds.segment_type,
ds.tablespace_name,
e.ext#, f.file#, e.block#, e.length * ds.blocksize, e.length, e.file#
from sys.uet$ e, sys.sys_dba_segs ds, sys.file$ f
where e.segfile# = ds.relative_fno
and e.segblock# = ds.header_block
and e.ts# = ds.tablespace_id
and e.ts# = f.ts#
and e.file# = f.relfile#
and bitand(NVL(ds.segment_flags,0), 1) = 0
and bitand(NVL(ds.segment_flags,0), 65536) = 0
union all
select
ds.owner, ds.segment_name, ds.partition_name, ds.segment_type,
ds.tablespace_name,
e.ktfbueextno, f.file#, e.ktfbuebno,
e.ktfbueblks * ds.blocksize, e.ktfbueblks, e.ktfbuefno
from sys.sys_dba_segs ds, sys.x$ktfbue e, sys.file$ f
where e.ktfbuesegfno = ds.relative_fno
and e.ktfbuesegbno = ds.header_block
and e.ktfbuesegtsn = ds.tablespace_id
and ds.tablespace_id = f.ts#
and e.ktfbuefno = f.relfile#
and bitand(NVL(ds.segment_flags, 0), 1) = 1
and bitand(NVL(ds.segment_flags,0), 65536) = 0
union all以上的部分对应于DMT tablespace,下面的部分对应于LMT tablespace。
DMT tablespace从uet$中读取extent信息,所以不受offline datafile的影响。
LMT tablespace从x$ktfbue中读取extent信息,对于x$ktfbue,描述为”Used extent bitmap in file header for LMT (equivalent to uet$ in DMT)”
我们来看看datafile offline前后,该表有什么变化:
— online datafile 10,多分配几个extent给表eagle_fan,并记录下一些信息
SQL> recover datafile 10
Media recovery complete.
SQL> alter database datafile 10 online;Database altered.
SQL> alter table eagle_fan allocate extent;
Table altered.
SQL> /
Table altered.
SQL> /
Table altered.
SQL> /
Table altered.
SQL> select extent_id,file_id,block_id from dba_extents where segment_name=’EAGLE_FAN’ order by extent_id;
EXTENT_ID FILE_ID BLOCK_ID
———- ———- ———-
0 10 7049
1 11 7177
2 10 7177
3 11 7305
4 10 7305SQL> select ktfbuefno file_id,count(*) from sys.x$ktfbue where ktfbuefno in (10,11) group by ktfbuefno;
FILE_ID COUNT(*)
———- ———-
10 58
11 58SQL> create table t as select * from sys.x$ktfbue where ktfbuefno in (10,11);
Table created.
— offline后再来比较,可以发现关于Eagle_Fan表的信息丢失
SQL> alter database datafile 10 offline;
Database altered.
SQL> select ktfbuefno file_id,count(*) from sys.x$ktfbue where ktfbuefno in (10,11) group by ktfbuefno;
FILE_ID COUNT(*)
———- ———-
11 56
10 55SQL> select e.ktfbuefno,e.ktfbuesegfno,e.ktfbuesegbno,e.ktfbuesegtsn from t e where ktfbuefno in (10,11) and
2 (ktfbuefno,ktfbuesegfno,ktfbuesegbno,ktfbuesegtsn)
3 not in (select ktfbuefno,ktfbuesegfno,ktfbuesegbno,ktfbuesegtsn from sys.x$ktfbue where ktfbuefno in (10,11))
4 ;KTFBUEFNO KTFBUESEGFNO KTFBUESEGBNO KTFBUESEGTSN
———- ———— ———— ————
10 10 7052 9
11 10 7052 9
10 10 7052 9
11 10 7052 9
10 10 7052 91* select header_file,header_block,segment_name from dba_segments where header_file=10 and header_block=7052
SQL> /HEADER_FILE HEADER_BLOCK SEGMENT_NAME
———– ———— ——————————
10 7052 EAGLE_FANSQL> select * from dba_extents where segment_name=’EAGLE_FAN';
no rows selected