經典故障:四個雷,3*2*2*3種隨機方法的特殊恢復案例

資料和雲發表於2020-06-29

導讀:恢復專家前輩給我們精心準備了個故障,埋了四個雷,整個恢復過程感覺像是雲霄飛車,整理分享至此,希望對大家有幫助。


恢復檔案

就給一個壓縮的system,起庫。

經典故障:四個雷,3*2*2*3種隨機方法的特殊恢復案例
經典故障:四個雷,3*2*2*3種隨機方法的特殊恢復案例


恢復過程

首先,獲取system檔案的字符集,資料庫名,然後建立引數檔案,重建控制檔案,這裡就不過多介紹,話不多說,先嚐試啟動資料庫。

第一個雷

  • SQL> startup nomount pfile='/gauss/init.ora';
    ORACLE instance started.
    Total System Global Area  396668928 bytes
    Fixed Size                  2253624 bytes
    Variable Size             125832392 bytes
    Database Buffers          264241152 bytes
    Redo Buffers                4341760 bytes
    SQL> @cf
    Control file created.
    ORA-00279: change 4936537 generated at 04/21/2020 00:03:57 needed for thread 1
    ORA-00289: suggestion :
    /guass/app/oracle/product/11.2.0/db_1/dbs/arch1_41_1033397865.dbf
    ORA-00280: change 4936537 for thread 1 is in sequence #41
    Specify log: {<RET>=suggested | filename | AUTO | CANCEL}
    cancel
    Media recovery cancelled.
    SQL> alter database open resetlogs;
    alter database open resetlogs
    *
    ERROR at line 1:
    ORA-01092: ORACLE instance terminated. Disconnection forced
    ORA-00704: bootstrap process failure
    ORA-00704: bootstrap process failure
    ORA-00604: error occurred at recursive SQL level 1
    ORA-01578: ORACLE data block corrupted (file # 1, block # 338)--壞塊
    ORA-01110: data file 1: '/gauss/system.dbf'
    Process ID: 32245
    Session ID: 1 Serial number: 3


資料啟動報file 1, block 338存在壞塊的錯誤,我們來查一下這個塊對應的物件。


  • TABLESPACE_NAME    SEGMENT_TYPE    OWNER         SEGMENT_NAME
    ------------------ --------------- ------------- --------------
    SYSTEM             INDEX           SYS           I_OBJ1
    這個物件是I_OBJ1,I_OBJ1是什麼?
    SQL> select * from bootstrap$ where SQL_TEXT like '%I_OBJ1%' order by  LINE#;
        LINE#       OBJ# SQL_TEXT
    ---------- ---------- --------------------------------------------------------------------------------
           36         36 CREATE UNIQUE INDEX I_OBJ1 ON OBJ$(OBJ#,OWNER#,TYPE#) PCTFREE 10 INITRANS 2 MAXT
                         RANS 255 STORAGE (  INITIAL 64K NEXT 1024K MINEXTENTS 1 MAXEXTENTS 2147483645 PC
                         TINCREASE 0 OBJNO 36 EXTENTS (FILE 1 BLOCK 336))

I_OBJ1是核心基表OBJ$的一個索引,當普通的索引出現壞塊,我們可以透過重建去處理,但是OBJ#<59 的索引出現壞塊,是不能夠透過rebuild的方式處理。資料庫open的情況下可以透過swap的方式處理,不過這裡我們資料庫都沒開啟。

首先我們先來介紹下資料庫啟動的過程:


  • 1、在system 表空間的第一個資料檔案的特定偏移位置,找到root dba變數
    2、root dba變數的值就是指向sys.bootstrap$表的物理位置的指標
    3、sys.bootstrap$表中記錄了資料庫基礎字典表的物理位置
    4、基礎字典表內記錄了使用者段段頭的物理儲存位置
    下一步就是要透過某個段的segment header block從資料檔案中直接讀出表的資料。
    在sys.bootstrap$表中記錄的資料字典表的create語句,還有一部分是沒有直接指定儲存位置的,比如簇成員。


那麼怎麼去爬過第一個雷?這裡有兩個方案:

方法一

最簡單的方法就是透過bbed cp同平臺同版本的相同塊去覆蓋。


BBED> copy file 6 block 338 to file 1 block 338


但是如果I_OBJ1存在大量的壞塊情況下,cp的效率比較低。

方法二

刪除I_OBJ1,但是我們這裡不是刪除IND$裡的I_OBJ1索引,再次重啟的時候又建立了,這裡我們需要刪除sys.bootstrap裡的索引。

  • BBED> x /rnnc *kdbr[1]
    rowdata[3681]                               @7322
    -------------
    flag@7322: 0x2c (KDRHFL, KDRHFF, KDRHFH)
    lock@7323: 0x01
    cols@7324:    3
    col    0[2] @7325: 36
    col    1[2] @7328: 36
    col  2[208] @7331: CREATE UNIQUE INDEX I_OBJ1 ON OBJ$(OBJ#,OWNER#,TYPE#) P
    CTFREE 10 INITRANS 2 MAXTRANS 255 STORAGE (  INITIAL 64K NEXT 1024K MINEXTE
    NTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 OBJNO 36 EXTENTS (FILE 1 BLOCK 33
    6))
    BBED> assign /x offset 7322 =0x3c --刪除索引
    ub1 rowdata[0]                              @7322     0x3c
    BBED>  x /rnnc dba 1,523 *kdbr[1]
    rowdata[3681]                               @7322
    -------------
    flag@7322: 0x3c (KDRHFL, KDRHFF, KDRHFD, KDRHFH)
    lock@7323: 0x01
    cols@7324:    0  --已經刪除
    BBED> sum apply
    Check value for File 1, Block 523:
    current = 0x7e06, required = 0x7e06


方法三


[ora11@zdata bin]$ strings $ORACLE_HOME/bin/oracle |wc -l1341571


11.2.0.4的執行檔案包含1341571個函式,可以透過修改oracle執行檔案指定<hint +full>,繞過索引I_OBJ1來處理。

第二個雷

透過上面兩種方式排了第一個雷,我們順利到達第二關。趕緊嘗試開啟,看看又會報什麼錯。


  • SQL> oradebug event 10046 trace name context forever,level 12;
    Statement processed.
    SQL> alter database open resetlogs;
    alter database open resetlogs
    *
    ERROR at line 1:
    ORA-01092: ORACLE instance terminated. Disconnection forced
    ORA-00704: bootstrap process failure
    ORA-00704: bootstrap process failure
    ORA-00604: error occurred at recursive SQL level 1
    ORA-01555: snapshot too old: rollback segment number 27 with name
    "$" too small
    Process ID: 17397
    Session ID: 1 Serial number: 5


ORA-01555快照過舊的報錯,這個故障熟悉又陌生,嘗試去推薦推進低位的scn,發現並沒有效果,看來這個雷埋的還是很深的。

當oracle讀取一個塊,是否需要undo來完成一致性讀,有很多種情況:

1. itl有活動事務,回查undo段頭發現事務確實是活動狀態,那麼肯定要undo來做一致性讀
2. itl有活動事務,回查undo段頭發現事務已經提交或者回滾,那麼需要做塊清除來確定commit scn,如果query scn<commit scn,則也需要undo來做一致性讀塊清除一直要找到大於query scn的commit scn,upper的也可以,才會做一致性讀 否則直接01555了
3. itl事務已經提交 flag為C/U/CU,則掃描itl的commit scn,如果發現有query scn<commit scn,則也需要undo來做一致性讀


  • WAIT #140193663907536: nam='db file sequential read' ela= 788 file#=1 block#=241 blocks=1 obj#=18 tim=1587333670688800
    =====================
    PARSING IN CURSOR #140193661586512 len=142 dep=2 uid=0 oct=3 lid=0 tim=1587333670689091 hv=361892850 ad='775596a0' sqlid='7bd391hat42zk'
    select /*+ rule */ name,file#,block#,status$,user#,undosqn,xactsqn,scnbas,scnwrp,DECODE(inst#,0,NULL,inst#),ts#,spare1 from undo$ where us#=:1
    END OF STMT
    PARSE #140193661586512:c=242,e=243,p=0,cr=0,cu=0,mis=1,r=0,dep=2,og=3,plh=0,tim=1587333670689090
    BINDS #140193661586512:



event 10046 trace 發現在訪問塊241的時候出現故障,這裡我們就去看看這個塊到底有什麼問題。

  • BBED> p ktbbh
    .........省略
            ub2 kxidusn                        @44       0x001b
            ub2 kxidslt                        @46       0x000b
            ub4 kxidsqn                        @48       0x00000186
         struct ktbituba, 8 bytes              @52
            ub4 kubadba                        @52       0x00c00a97
            ub2 kubaseq                        @56       0x0219
            ub1 kubarec                        @58       0x26
         ub2 ktbitflg                          @60       0x2001 (KTBFUPB)
         union _ktbitun, 2 bytes               @62
            sb2 _ktbitfsc                      @62       0
            ub2 _ktbitwrp                      @62       0x0000
         ub4 ktbitbas                          @64       0xffffffff --這裡被改成0xffffffff



這裡低位scn都改成最大值了,難怪怎麼poke scn都沒效果。。這個雷特麼陰險,太壞了。

好了我們知道問題所在了,就是ktbitbas被人工改成0xffffffff,那麼恢復方案也有兩種。

方法一

修改scn,我們手動修改ktbitbas的值。



BBED> assign ktbbhitl[0].ktbitbas=0x004b531fub4 ktbitbas                                @64       0x004b531f


方法二

既然低位已經最大了,再怎麼推也不會超過0xffffffff,那麼我們就嘗試poke推進高位scn。

SQL> oradebug setmypid
SQL> oradebug dumpvar sga kcsgscn_
kcslf kcsgscn_ [06001AE70, 06001AEA0) = 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 6001AB50 00000000
SQL> oradebug poke 0x06001AE74 4 0x50
BEFORE: [06001AE70, 06001AE78) = 00000000 00000000
AFTER:  [06001AE70, 06001AE78) = 004B60E0 00000000


第三個雷


  • SQL> alter database open resetlogs;
    alter database open resetlogs
    *
    ERROR at line 1:
    ORA-01092: ORACLE instance terminated. Disconnection forced
    ORA-01173: data dictionary indicates missing data file from system tablespace
    Process ID: 26639
    Session ID: 1 Serial number: 3



看到這個報錯,說明順利進入第三關

方法一

透過一鍵指令碼設定*._corrupted_rollback_segments隱患引數,遮蔽回滾段。


*._corrupted_rollback_segments='_SYSSMU28_79026890$','_SYSSMU24_100127047$','_SYSSMU28_79026890$','_SYSSMU21_1449495591$','_SYSSMU24_100127047$','_SYSSMU21_1449495591$','_SYSSMU30_493042799$','_SYSSMU30_493042799$','_SYSSMU23_1725104698$','_SYSSMU23_1725104698$','_SYSSMU22_3628056578$','_SYSSMU22_3628056578$','_SYSSMU25_3360715651$','_SYSSMU22_3628056578$','_SYSSMU25_3360715651$','_SYSSMU22_36280565.......


方法二


  • SQL> select OWNER,SEGMENT_NAME,FILE_ID,BLOCK_ID from dba_extents where segment_name='UNDO$';
    OWNER      SEGMENT_NAME       FILE_ID   BLOCK_ID
    ---------- --------------- ---------- ----------
    SYS        UNDO$                    1        224
    BBED> p ktetb
    struct ktetb[0], 8 bytes                    @108
      ub4 ktetbdba                             @108      0x004000e1--extent的首地址
      ub4 ktetbnbk                             @112      0x00000007--連續7個塊
    BBED> x /rnc *kdbr[0
    rowdata[2337]                               @8146
    -------------
    flag@8146: 0x2c (KDRHFL, KDRHFF, KDRHFH)
    lock@8147: 0x00
    cols@8148:   17
    col    0[1] @8149: 0
    col    1[6] @8151: SYSTEM
    col    2[1] @8158: .
    col    3[2] @8160: ..
    ......省略
    BBED> x /rnc *kdbr[1
    rowdata[2267]                               @8076
    -------------
    flag@8076: 0x2c (KDRHFL, KDRHFF, KDRHFH)
    lock@8077: 0x00
    cols@8078:   17
    col    0[2] @8079: 1
    col   1[19] @8082: _SYSSMU1_770609302$  --需要刪除
    col    2[2] @8102: ..
    col    3[2] @8105: ..
    col    4[3] @8108: ...
    ......省略




Oracle資料庫服務程式在定位某行資料時,採用了資料字典+段頭extent map的“集中—分散”儲存模式,具體為:

1、在資料字典中存放資料段(對應表或分割槽)頭的物理位置
2、在資料段頭存放段中所有extent(即連續的block集合)的物理位置

ktetb其實就是UNDO$的extent map,該結構體記錄了segment中的extent的首地址。
透過一鍵指令碼找到dba 0x004000e1,也就是1,225,然後連續7個塊,刪除裡面除了system的其他所有回滾段,這種方法實際操作太麻煩,大家有興趣可以挑戰下。

第四個雷

趟過前三個雷,恭喜順利達到第四個雷。這個雷來者不善,畢竟是終極BOSS,處理完這個就要順利通關了,勝利在望。


  • SQL> alter database open resetlogs;
    alter database open resetlogs
    *
    ERROR at line 1:
    ORA-03113: end-of-file on communication channel
    Process ID: 26280
    Session ID: 1 Serial number: 3




這個報錯,資料庫直接掛了,並沒有出現什麼錯誤,設定個10046事件去追蹤下。


  • SQL> oradebug event 10046 trace name context forever,level 12;
    SQL> alter database open resetlogs;
    alter database open resetlogs
    --後臺alert的報錯
    SMON: enabling cache recovery
    Exception [type: SIGSEGV, Address not mapped to object] [ADDR:0xD0C5CD7] [PC:0x97DF62E, kgebse()+776] [flags: 0x2, count: 2]
    Fri Apr 24 06:42:13 2020
    PMON (ospid: 19501): terminating the instance due to error 397
    --file#=1 block#=140需要關注
    WAIT #140452738872120: nam='db file sequential read' ela= 670 file#=1 block#=140 blocks=1 obj#=0 tim=1587681730297305
    Exception [type: SIGSEGV, Address not mapped to object] [ADDR:0xD0C5CD7] [PC:0x97E0BBA, kgegpa()+40] [flags: 0x0, count: 1]
    DDE previous invocation failed before phase II
    DDE was called in a 'No Invocation Mode'
    ----- Start Diag Diagnostic Dump -----
    Diag diagnostic dump is performed due to an error in the diagfw code during error handling.
    DDE is switched to protected mode during the diagnostic dump to prevent recursive errors in the error hadnling code.




從SMON: enabling cache recovery這裡其實也能看出來,資料庫正在做一些回滾操作,file 1 block 140 對應的是system回滾段,訪問到140塊的時候資料庫直接當機。


TABLESPACE_NAME      SEGMENT_TYPE       OWNER      SEGMENT_NAME-------------------- ------------------ ---------- ---------------SYSTEM               ROLLBACK           SYS        SYSTEM

我來看窺探file 1 block 140 到底有什麼問題。


  • BBED> p dba 1,140 ktubh
    struct ktubh, 22 bytes                      @20
      struct ktubhxid, 8 bytes                 @20
         ub2 kxidusn                           @20       0x0000
         ub2 kxidslt                           @22       0x003b
         ub4 kxidsqn                           @24       0x0000002b
      ub2 ktubhseq                             @28       0x0025  --seq 是0x0025
      ub1 ktubhcnt                             @30       0x03
      ub1 ktubhirb                             @31       0x03
      ub1 ktubhicl                             @32       0x00
      ub1 ktubhflg                             @33       0x00
    BBED> p dba 1,128 ktuxc
    struct ktuxc, 104 bytes                     @4148
      struct ktuxcscn, 8 bytes                 @4148
         ub4 kscnbas                           @4148     0x004996f7
         ub2 kscnwrp                           @4152     0x0000
      .....省略
      struct ktuxcfbp[0], 12 bytes             @4192
         struct ktufbuba, 8 bytes              @4192
            ub4 kubadba                        @4192     0x0040008c
            ub2 kubaseq                        @4196     0x0030   --seq 是0x0030
            ub1 kubarec                        @4198     0x03
         sb2 ktufbext                          @4200     1
         sb2 ktufbspc                          @4202     7340




這裡感覺是人為把128塊的offset 4196由0x0025改成了0x0030

有三種方案恢復
方法一

把128塊的offset 4196從0x0030改成了0x0025





BBED> assign dba 1,128 4196=0x0025
ub2 kubaseq                                 @4196     0x0025

方法二

需要修改ktuxcnfb和ktuxcfbp[1] 即可。其中將ktuxcnfb修改為0,ktuxcfbp[1]中的uba修改為0,表示回滾的時候讓oracle認為沒有可以分配的undo塊,相當於不回滾。




BBED> modify /x 00 offset 4168
BBED> modify /x 000000 offset 4192

方法三

透過strace跟蹤,得到如下的trace。

  • 01:13:27 write(14, "[32]: ktuiup []", 15) = 15 <0.000012>  --ktuiup
    01:13:27 write(15, "!gF\n", 4)          = 4 <0.000011>
    01:13:27 lseek(14, 0, SEEK_CUR)         = 3425947 <0.000009>
    01:13:27 write(14, "\n", 1)             = 1 <0.000011>
    01:13:27 write(15, "!A1\n", 4)          = 4 <0.000012>
    01:13:27 lseek(14, 0, SEEK_CUR)         = 3425948 <0.000010>
    01:13:27 write(14, "[33]: ktuini []", 15) = 15 <0.000012>    ------注意這裡ktuini函式
    01:13:27 write(15, "!gF\n", 4)          = 4 <0.000011>
    01:13:27 lseek(14, 0, SEEK_CUR)         = 3425963 <0.000009>
    01:13:27 write(14, "\n", 1)             = 1 <0.000012>
    01:13:27 write(15, "!A1\n", 4)          = 4 <0.000012>
    01:13:27 lseek(14, 0, SEEK_CUR)         = 3425964 <0.000010>
    01:13:27 write(14, "[34]: adbdrv []", 15) = 15 <0.000011>
    01:13:27 write(15, "!gF\n", 4)          = 4 <0.000012>
    01:13:27 lseek(14, 0, SEEK_CUR)         = 3425979 <0.000009>
    01:13:27 write(14, "\n", 1)             = 1 <0.000012>
    01:13:27 write(15, "!A1\n", 4)          = 4 <0.000011>
    01:13:27 lseek(14, 0, SEEK_CUR)         = 3425980 <0.000009>
    01:13:27 write(14, "[35]: opiexe []", 15) = 15 <0.000012>
    01:13:27 write(15, "!gF\n", 4)          = 4 <0.000011>
    01:13:27 lseek(14, 0, SEEK_CUR)         = 3425995 <0.000009>
    01:13:27 write(14, "\n", 1)             = 1 <0.000012>




注意這裡ktuini函式,SEEK_CUR表示檔案的相對當前位置,這裡也能看出來資料庫再做一些recovery的動作,呼叫ktuiup函式,開始恢復回滾段上的死事務。

既然資料庫呼叫函式ktuini報錯,那我們就透過再這個函式設定斷點,然後匯出資料。


--session 1
(gdb) break ktuini
Breakpoint 1 at 0xf21352
(gdb) c
Continuing.
--session 2
匯出資料
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
exp \'/ as sysdba \' file=/home/ora11/meta.dmp ROWS=n  buffer=102400000


總結

透過這個3x2x2x3種隨機方法來處理這個異常恢復案例分享,希望大家有所收穫。另外說個題外話,在雲和恩墨工作很有挑戰,尤其晉級的時候,獨享與多位大咖交流,面對三小時靈魂式拷問,那種全方位沉浸式體驗讓人無法忘卻。歡迎有志之士,各位踴躍加入恩墨大家庭,we are family!

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31556440/viewspace-2701008/,如需轉載,請註明出處,否則將追究法律責任。

相關文章