天天看點

深入分析:12C ASM Normal備援中PDB檔案塊号與AU關系與恢複

在 10G 和 11G 中,DBA 可以根據檔案名,确定這個檔案在 ASM 磁盤組上的分布,然後 dd 出來每一個 AU,最後拼湊成一個完成的資料檔案。

在 12C 的 PDB 中,我們嘗試用這種方法,進一步,根據給定的檔案号和表的塊号,從 ASM 磁盤上 dd 出來這些塊。之後 sqlplus 清空這個表,再将出來的資料 dd 回去,以驗證是否準确找到了需要的 block。

我們可以推算出表塊跟 AU 的關系。

環境準備

測試環境是 8 KB 的标準塊,有一個 5 個磁盤做 Normal 備援的磁盤組,AU 大小為 4M。

PDB 生成的測試資料将要放在這個磁盤組。

如果隻有一個盤的磁盤組,或者外部備援,就沒有必要測試了。

--這個是 pdb,要去 pdb 裡面查詢。

--在我的環境在 PDB 裡面建立表空間,檔案号 21。

alter session set container=ora122pdb1;
SQL> create tablespace asmres datafile '+DATAC1' SIZE 100M  AUTOEXTEND ON;
Tablespace created.
           

然後查詢檔案名為 asmres.271.978291939,檔案号為 21.

下面建立測試用表。測試用表這裡,先建立空表,再給某個表打數,構造一個看起來較為錯亂的 extent 分布。

create table asmtable tablespace asmres as select * from dba_objects where 0=1;

create table asmtable2 tablespace asmres as select * from asmtable;

create table asmtable3 tablespace asmres as select * from asmtable;

create table asmtable4 tablespace asmres as select * from asmtable;

insert into asmtable select * from dba_objects where rownum<512;

檢視這個幾個表的 extents 分布。

SQL> select owner,SEGMENT_NAME,EXTENT_ID,FILE_ID,BLOCK_ID,BLOCKS,RELATIVE_FNO from dba_extents where SEGMENT_NAME like 'ASMTABLE%';

這個構造的場景,能比較明顯看出來,extent 的分布規律。

注意,從這裡可以看到,ASMTABLE 生成了第二個 extent。

--注意這裡的 extend_id 跟 ASM 的 extend_number 是兩個概念。不可以混用。

是以,ASMTABLE 這個表是由 21 号檔案的,128,129,130,131,132,133,134,135,注意這裡不連續,160,161,162,163,164,165,166,167,這16個塊構成。

這些是 DB 層的概念,ASM 層的概念跟這個不一樣,是以通過視圖,無法對應出來。但是,可以推測。

驗證

下面開始驗證:

首先,拿出 21 号檔案的 AU 分布圖。

set linesize 255 pagesize 9999
col "FILE_NAME" format a30
col path for a25
col name for a10
set numw 15
set head on
select     a.NAME         "FILE_NAME",
           p.NUMBER_KFFXP "FILE_NUMBER",
           p.DISK_KFFXP   "DISK_NUMBER",
           p.AU_KFFXP     "AU_NUMBER",
           p.XNUM_KFFXP   "EXTENT_NUMBER",
           p.LXN_KFFXP    "P/S_EXTENT",
           a.group_number,
           d.path,
           dg.ALLOCATION_UNIT_SIZE/1048576  "AU_SIEZ_MB",
           dg.name
      from x$kffxp p, v$asm_alias a,v$asm_disk d,v$asm_diskgroup dg
     where p.GROUP_KFFXP = a.GROUP_NUMBER
       and p.NUMBER_KFFXP = a.FILE_NUMBER
       and a.name like ('ASMRES.271.978291939')
       and p.LXN_KFFXP=0 --隻看primary的AU。1st_mirror什麼的去掉。
       and d.GROUP_NUMBER=p.GROUP_KFFXP
       and dg.GROUP_NUMBER=p.GROUP_KFFXP
       and p.DISK_KFFXP=d.disk_number
   order by  EXTENT_NUMBER,DISK_KFFXP,AU_KFFXP;
           

分析結果

這裡查出來 26 個 AU,每個 AU 是 4M,總共 104M。為什麼不是資料檔案的100M ?

注意,這裡的檔案的 AU 在 ASM 磁盤組中的分布看起來是錯亂的,基于 ASM 的算法,你會發現,AU 的分布是在磁盤 4,0,3,2,1 這樣循環方式,打到每個成員盤上。

這裡我過濾掉了主 AU 的 mirror,如果不過濾的話,另一組的規律也同樣明顯。

這裡就是前面說的,沒有直接的視圖來對應資料檔案塊跟 AU 的關系,需要推測。

測試分兩步,第一步是拿出來完整連續的檔案,第二步是從檔案裡拿到表的塊。

--Oracle 内部以及常做 ASM 恢複的 DBA 都有 Oracle 内部的一個腳本,可以直接從ASM 上拿出需要的塊,但是那個,我沒有。:)

首先,extent_number 的連續等于檔案的連續。就是說按照連續 extent_number 方式,去對應的盤上找到對應的 AU,拿出來。再按照順序組合。就是一個連續的資料檔案。

下面是過程。

首先根據 AU 号把 dd 磁盤的指令生成出來,這裡注意,最重要的是 order by 那一句 。以及 skip。

Select 'dd if='||d.path||' bs=4194304 count=1 skip='||p.au_kffxp||' of=asmres_'||p.XNUM_KFFXP||'.dbf'
from x$kffxp p, v$asm_alias a,v$asm_disk d,v$asm_diskgroup dg
     where p.GROUP_KFFXP = a.GROUP_NUMBER
       and p.NUMBER_KFFXP = a.FILE_NUMBER
       and a.name in ('ASMRES.271.978291939')
       and p.LXN_KFFXP=0
       and d.GROUP_NUMBER=p.GROUP_KFFXP
       and dg.GROUP_NUMBER=p.GROUP_KFFXP
       and p.DISK_KFFXP=d.disk_number
order by XNUM_KFFXP;
           

生成的指令如下:

dd if=/dev/mapper/data07 bs=4194304 count=1 skip=321 of=asmres_0.dbf
dd if=/dev/mapper/data03 bs=4194304 count=1 skip=317 of=asmres_1.dbf
dd if=/dev/mapper/data06 bs=4194304 count=1 skip=325 of=asmres_2.dbf
dd if=/dev/mapper/data05 bs=4194304 count=1 skip=339 of=asmres_3.dbf
dd if=/dev/mapper/data04 bs=4194304 count=1 skip=315 of=asmres_4.dbf
dd if=/dev/mapper/data07 bs=4194304 count=1 skip=324 of=asmres_5.dbf
dd if=/dev/mapper/data03 bs=4194304 count=1 skip=319 of=asmres_6.dbf
dd if=/dev/mapper/data06 bs=4194304 count=1 skip=326 of=asmres_7.dbf
dd if=/dev/mapper/data05 bs=4194304 count=1 skip=340 of=asmres_8.dbf
dd if=/dev/mapper/data04 bs=4194304 count=1 skip=317 of=asmres_9.dbf
dd if=/dev/mapper/data07 bs=4194304 count=1 skip=327 of=asmres_10.dbf
dd if=/dev/mapper/data03 bs=4194304 count=1 skip=322 of=asmres_11.dbf
dd if=/dev/mapper/data06 bs=4194304 count=1 skip=304 of=asmres_12.dbf
dd if=/dev/mapper/data05 bs=4194304 count=1 skip=342 of=asmres_13.dbf
dd if=/dev/mapper/data04 bs=4194304 count=1 skip=319 of=asmres_14.dbf
dd if=/dev/mapper/data07 bs=4194304 count=1 skip=304 of=asmres_15.dbf
dd if=/dev/mapper/data03 bs=4194304 count=1 skip=324 of=asmres_16.dbf
dd if=/dev/mapper/data06 bs=4194304 count=1 skip=306 of=asmres_17.dbf
dd if=/dev/mapper/data05 bs=4194304 count=1 skip=177 of=asmres_18.dbf
dd if=/dev/mapper/data04 bs=4194304 count=1 skip=322 of=asmres_19.dbf
dd if=/dev/mapper/data07 bs=4194304 count=1 skip=305 of=asmres_20.dbf
dd if=/dev/mapper/data03 bs=4194304 count=1 skip=325 of=asmres_21.dbf
dd if=/dev/mapper/data06 bs=4194304 count=1 skip=309 of=asmres_22.dbf
dd if=/dev/mapper/data05 bs=4194304 count=1 skip=179 of=asmres_23.dbf
dd if=/dev/mapper/data04 bs=4194304 count=1 skip=323 of=asmres_24.dbf
dd if=/dev/mapper/data07 bs=4194304 count=1 skip=308 of=asmres_25.dbf
           

然後再生成 dd 合成檔案的 SQL,注意裡面的 seek。

SELECT
'dd if=asmres_'||p.XNUM_KFFXP||'.dbf bs=4194304 count=1 seek='||p.XNUM_KFFXP||' of=asmres.all.dbf'
from x$kffxp p, v$asm_alias a,v$asm_disk d,v$asm_diskgroup dg
     where p.GROUP_KFFXP = a.GROUP_NUMBER
       and p.NUMBER_KFFXP = a.FILE_NUMBER
       and a.name in ('ASMRES.271.978291939')
       and p.LXN_KFFXP=0
       and d.GROUP_NUMBER=p.GROUP_KFFXP
       and dg.GROUP_NUMBER=p.GROUP_KFFXP
       and p.DISK_KFFXP=d.disk_number
order by XNUM_KFFXP;
           

生成的結果如下:

dd if=asmres_0.dbf bs=4194304 count=1 seek=0 of=asmres.all.dbf
dd if=asmres_1.dbf bs=4194304 count=1 seek=1 of=asmres.all.dbf
dd if=asmres_2.dbf bs=4194304 count=1 seek=2 of=asmres.all.dbf
dd if=asmres_3.dbf bs=4194304 count=1 seek=3 of=asmres.all.dbf
dd if=asmres_4.dbf bs=4194304 count=1 seek=4 of=asmres.all.dbf
dd if=asmres_5.dbf bs=4194304 count=1 seek=5 of=asmres.all.dbf
dd if=asmres_6.dbf bs=4194304 count=1 seek=6 of=asmres.all.dbf
dd if=asmres_7.dbf bs=4194304 count=1 seek=7 of=asmres.all.dbf
dd if=asmres_8.dbf bs=4194304 count=1 seek=8 of=asmres.all.dbf
dd if=asmres_9.dbf bs=4194304 count=1 seek=9 of=asmres.all.dbf
dd if=asmres_10.dbf bs=4194304 count=1 seek=10 of=asmres.all.dbf
dd if=asmres_11.dbf bs=4194304 count=1 seek=11 of=asmres.all.dbf
dd if=asmres_12.dbf bs=4194304 count=1 seek=12 of=asmres.all.dbf
dd if=asmres_13.dbf bs=4194304 count=1 seek=13 of=asmres.all.dbf
dd if=asmres_14.dbf bs=4194304 count=1 seek=14 of=asmres.all.dbf
dd if=asmres_15.dbf bs=4194304 count=1 seek=15 of=asmres.all.dbf
dd if=asmres_16.dbf bs=4194304 count=1 seek=16 of=asmres.all.dbf
dd if=asmres_17.dbf bs=4194304 count=1 seek=17 of=asmres.all.dbf
dd if=asmres_18.dbf bs=4194304 count=1 seek=18 of=asmres.all.dbf
dd if=asmres_19.dbf bs=4194304 count=1 seek=19 of=asmres.all.dbf
dd if=asmres_20.dbf bs=4194304 count=1 seek=20 of=asmres.all.dbf
dd if=asmres_21.dbf bs=4194304 count=1 seek=21 of=asmres.all.dbf
dd if=asmres_22.dbf bs=4194304 count=1 seek=22 of=asmres.all.dbf
dd if=asmres_23.dbf bs=4194304 count=1 seek=23 of=asmres.all.dbf
dd if=asmres_24.dbf bs=4194304 count=1 seek=24 of=asmres.all.dbf
dd if=asmres_25.dbf bs=4194304 count=1 seek=25 of=asmres.all.dbf
           

執行了之後,會得到一個 104M 的 asmres.all.dbf(你猜為什麼不是當初建立的 100M ? )

拼接好了之後,開始 dbv

[grid@tafrac121 ~]$ dbv file=asmres.all.dbf
DBVERIFY: Release 12.2.0.1.0 - Production on Fri Jun 8 22:15:37 2018
Copyright (c) 1982, 2017, Oracle and/or its affiliates.  All rights reserved.
DBVERIFY - Verification starting : FILE = /home/grid/asmres.all.dbf
DBVERIFY - Verification complete
Total Pages Examined         : 12800
Total Pages Processed (Data) : 1409
Total Pages Failing   (Data) : 0
Total Pages Processed (Index): 0
Total Pages Failing   (Index): 0
Total Pages Processed (Other): 166
Total Pages Processed (Seg)  : 0
Total Pages Failing   (Seg)  : 0
Total Pages Empty            : 11225
Total Pages Marked Corrupt   : 0
Total Pages Influx           : 0
Total Pages Encrypted        : 0
Highest block SCN            : 2207155 (0.2207155)
[grid@tafrac121 ~]$
           

資料檔案正常。

剛才說到 ASMTABLE 這個表是由 21 号檔案的,128,129,130,131,132,133,134,135,注意這裡不連續,160,161,162,163,164,165,166,167,這16個塊構成。

以下是羅列的一些知識點:

資料檔案上的塊号也是連續的。

到這裡就能把表的塊号跟實際磁盤和AU對應起來了。

例如,假如說5号檔案的17520塊是哪個磁盤的哪個AU:

(17520*block_size)/au_size,就能得到extent_number号,根據分布圖,找這個extent_number對應的AU和磁盤即可。

--但是注意:11.1以後,出現了可變extent,就是說在extent較大的時候,一個extent裡有多個AU,具體數值應該是1M的AU size的時候,資料檔案超過20G會觸發可變extent。

附加驗證:

用 dd 的方法,把一個表 dd 出來,然後,把這個表 delete 掉,然後再拿 dd 出來的塊,覆寫回去,看看資料還在不在。

前面說過,資料庫是 8192 的,AU 是 4M,是以一個 AU 可以存放,4*1024/8=512個塊。 也就是說,這個表的所有塊都在第一個 AU 裡。

就是前面的這個 AU:

FILE_NAME                                    FILE_NUMBER     DISK_NUMBER       AU_NUMBER   EXTENT_NUMBER      P/S_EXTENT    GROUP_NUMBER PATH                                              AU_SIEZ_MB NAME
ASMRES.271.978291939                                 271               4             321               0               0               1 /dev/mapper/data07                                         4 DATAC1
           

在 /dev/mapper/data07 盤上的 .AU 号是 321。

下面我們從資料檔案上的位置,dd 出來這個表的資料。然後 SQLPLUS 清空這個表,再把 dd 出來的表塊放回去。以驗證我們推測的分布是正确的。

注意知識點:

ASM 檔案跟放在檔案系統上的資料檔案,頭部不一樣,ASM 上的檔案頭部會多出來一個塊。

比如建立一個 10M 的資料檔案,在檔案系統上是 10240K,在 ASM 上,這個檔案的size 是10240+8K。

下面從已經 dd 出來的資料檔案上,把表的 16 個塊都 dd 出來。

dd if=asmres.all.dbf of=8_block_1.dd  skip=129  bs=8192 count=8
dd if=asmres.all.dbf of=8_block_2.dd  skip=161  bs=8192 count=8
 
SQL> select BYTES/1024 from dba_segments where segment_name='ASMTABLE';
 
     BYTES/1024
---------------
            128
           

把這個表,delete,清空掉。(如果這裡用 truncate 會影響後面的結果不?)

SQL> delete from asmtable;
511 rows deleted.
SQL> commit;
Commit complete.
SQL> select count(*) from asmtable;
 
       COUNT(*)
---------------
              0
           

然後把剛才 dd 出來的檔案覆寫回去。覆寫 /dev/mapper/data07 這個盤的 321 号AU,為了友善,我們把這個 AU 拿出來。

dd if=/dev/mapper/data07 bs=4194304 count=1 skip=321 of=AU_321_0.dbf
           

把上面提取出來的表的兩個 dd 寫進這個 AU 的 dd,再把這個 AU 的 dd 寫回磁盤。

dd if=asmres.all.dbf of=8_block_1.dd  skip=129  bs=8192 count=8
dd if=asmres.all.dbf of=8_block_2.dd  skip=161  bs=8192 count=8
 
dd if=8_block_1.dd of=AU_321_0.dbf seek=129 bs=8192 count=8 conv=notrunc
dd if=8_block_2.dd of=AU_321_0.dbf seek=161 bs=8192 count=8 conv=notrunc
 
dd if=AU_321_0.dbf bs=4194304 count=1 seek=321 of=/dev/mapper/data07 conv=notrunc
           

然後重新開機整個資料庫。

SQL> alter session set container=ora122pdb1;
Session altered.
SQL> select count(*) from asmtable;
 
  COUNT(*)
----------
       511
           

驗證完畢,這樣方式對應的 block 号和 au 号是正确的。

原文釋出時間為:2018-07-10

本文作者:李敏

本文來自雲栖社群合作夥伴“

資料和雲

”,了解相關資訊可以關注“

”。