揭祕Oracle資料庫truncate原理

lhrbest發表於2016-04-13

第一章 Oracle核心系列2-揭祕Oracle資料庫truncate原理

DBAplus社群 | 2015-11-30 23:45

 一、Truncate簡介

無數次事故告訴我們,Truncate是一項很危險的動作。一不小心搞錯,就會帶來毀滅性的打擊。我們都知道當Truncate命令發起之後,Oracle實際上並沒有在刪除底層資料塊上的資料,而是要等到重用的時候才會把這一部分資料回收,於是這給了我們一個能夠恢復資料庫的機會。

wps3772.tmp

二、知己知彼

對於Truncate單表來說,其實就相當於摧毀我們的一個段,我們資料庫中的物理結構是由段區塊三個構成的。首先我們要最需要了解的就是一張“圖譜”?圖譜是什麼?就是一個段的組成結構,而最能表現段的構成的就是一個三級點陣圖塊的結構圖。

wps3793.tmp

我們可以看到一個段最先是由段頭塊構成的,段頭其實就是第1個L3塊,只有當這些無法記錄下的時候,才會產生第2個L3級塊,不過這種情況非常少見,段頭塊指向了若干的L2點陣圖塊,每個L2點陣圖塊又指向了若干L1點陣圖塊,L1點陣圖塊則指向了真正的資料塊。

1.建立實驗環境所需要的表

wps37A3.tmp

實驗環境建立完成之後,我們就需要摸清楚我們的這個段A的點陣圖及資料塊分佈情況,我們不直接從檢視裡面查,這裡我們考慮需要通過bbed的方式從資料塊中獲取點陣圖及資料塊分佈情況。

2.段頭塊/L3點陣圖塊指向了哪些L2點陣圖塊

段由哪些區間構成?這個資訊我們需要從段頭塊中獲取出來。當你建立一個段後,即使你沒有往裡面插入任何資料,系統也是會預先分配一些區給你的。所以段頭塊是那個塊,我們可以通過dba_segments查詢出來。就算truncate了這個段,我們仍然能夠從dba_segments中查詢到段頭的資訊。

wps37A4.tmp

找到了段頭塊,我們就可以用bbed挖掘下資訊。

wps37B5.tmp

wps37B6.tmp

這裡我們可以看到段頭塊的第一個offset是23。那麼我們的段頭塊指向的L2點陣圖塊在offset 5192的位置。這裡請記住段頭塊的標示是23。

wps37C7.tmp

從這裡我們既可以找到我們的L2點陣圖塊,81004001,這裡只有1個L2塊,因為後面都是00000000(空),因為涉及到作業系統位元組序的問題,這裡需要轉換換成01400081。轉換後我們可以使用下列查詢找到檔案號和塊號。

wps37C8.tmp

3.L2點陣圖塊指向了哪些L1點陣圖塊

接下來我們可以繼續讀我們的L2點陣圖塊來尋找我們的L1點陣圖塊。可以看到L2的第一個offset是21。請記住L2點陣圖塊的標示是21。

wps37D8.tmp

L2指向L1資料塊的位置從offset 116開始。到哪兒結束需要看後面有沒有00000000(空)

wps37D9.tmp

從dump出來的地方我們來看,前面是一個地址,後面跟著是01000100,比較規律,大概7組之後就變成00000000(空)了。 跟上面一樣因為位元組序的問題,這裡我們需要將80004001轉換成01400080。然後我們通過下列查詢得到了區塊的位置。而後面的01000100,前面的01則這個L1下面的塊全部填滿,無空資料塊,後面的01則代表這個塊是instance 1產生的。而最後的一個05000100,05則代表著這個L1下面還有空塊,可以插入。而後面的01我們說過代表著是instance。如果這個系統是個rac的系統,節點2也插入了資料,那麼這裡就會顯示05000200。

wps37EA.tmp

wps37EB.tmp

通過上述查詢,我們找到了7個L1塊的資訊。

4.L1點陣圖塊指向了哪些資料塊

前面我們查到了我們的L2塊上指向的L1塊,並且清楚的知道哪個L1下面是滿的,哪個L1下面還有空閒塊。我們就從拿最後一個有空閒塊的L1點陣圖塊進行分析。

wps37EC.tmp

可以看到L1的第一個offset是20。請記住L1點陣圖塊的標示是20。

L1指向資料塊的位置從offset 204開始。到哪兒結束需要看後面有沒有00000000(空)

wps37FC.tmp

大家可以看到這裡的值是e0004001,08000000,00000000,下一組值是e8004001,08000000,08000000,然後面就是00000000(空)。

這兩個的意思是告訴我們L1指向資料塊的起始位置,比如e0004001,就是檔案5,塊224,也就是它自己本身。08000000就代表著這個塊後面的連續7個塊都是的。而e8004001,08000000,08000000,就是檔案5,塊232,08000000就代表著這個塊後面的連續7個塊也是的。而最後一個08000000則代表著offset,這裡我們可以不用去管它。所以這裡我們就能夠知道我們的L1塊下面具體的資料塊有:224(它本身),225,226,227,228,229,230,231,232,233,234,235,236,237,238,239。我們來用下列語句查證一下。

wps37FD.tmp

可以看到223後面直接就是225,直接此處跳空,這是因為我們的224是L1點陣圖塊,後面緊跟著我們剛剛說的225,226,227,228,229,230,231,232,233。但是問題是,這裡看不到後面的234到239?這是因為234到239還是空閒沒有格式化過的塊,但是它已經被L1鎖定了。那麼我們的L1能不能看到這些情況呢?我們可以觀察offset 396。

wps37FE.tmp

這裡可以看到的是11111111,11000000。那麼這個代表什麼意思呢?如果這個塊是full的話,就是1,是unformatted的話就是0,正好和我們前面看到的吻合。

wps380F.tmp

三、Truncate如何恢復?

1.Data Object ID恢復

前面我們介紹了我們的段的組成形式,這裡來總結一下,首先是段頭塊,它指向了L2塊,L2塊指向了L1塊,而L1塊則指向了我們真實的物理資料塊。試著想想,如果我們發起truncate之後,Oracle會怎麼做?

wps3810.tmp

我們先來看一下物件,當我們建立物件的之後,OBJECT_ID和data_object_id都會是一樣的,但是當我們發生truncate之後,我們的object_id不會變,而data_object_id則會變掉。

wps3811.tmp

這裡可以看到我們Truncate表之後,data_object_id從75722變成了75727。我們分別看一下我們的段頭塊,L2點陣圖塊,L1點陣圖塊,資料塊,這個ID是否有變化。分別從檢查塊130,129,224,225

wps3822.tmp

wps3823.tmp

wps3833.tmp

wps3834.tmp

wps3845.tmp

wps3846.tmp

通過對各個塊的dump,發現段頭和L2點陣圖塊的data_obj_id已經發生了改變,從ca270100變成了cf270100,而只有第一個L1發生了變化,資料塊則沒有發生改變。

那我們是不是把data_obj_id修改回來就能夠恢復資料呢?

wps3857.tmp

wps3858.tmp

這裡還需要修改資料字典,否則會報錯。

wps3868.tmp

wps3869.tmp

wps387A.tmp

2.段頭高水位資訊恢復

修改了這些東西后,我們會發現資料還是沒有。我們還需要修改一些資訊。一個很重要的資訊就是段頭上的高水位資訊。在Truncate之前,段頭上會記載。前面我們在講L1塊的時候說過:

可以看到223後面直接就是225,直接此處跳空,這是因為我們的224是L1點陣圖塊,後面緊跟著我們剛剛說的225,226,227,228,229,230,231,232,233。但是問題是,這裡看不到後面的234到239?這是因為234到239還是空閒沒有格式化過的塊,但是它已經被L1鎖定了。

所以我們現在的高水位的塊是234,一般做全表掃描的查詢就會查高水位以下(234)的塊。我們來看下我們現在的高水位。

wps387B.tmp

注意看這裡的高水位是83004001,轉換成檔案號和塊號,剛好是檔案5塊131。而前面的00000000,03000000,則代表著是擴充套件0,block 3,代表著高水位的位置。剛好是第一個extent的第三個塊。128是L1,129是L2,130是段頭塊。而131則是第一個可以使用的資料塊。所以這裡記錄了extent 0,block為3則代表了檔案5的131號塊。而08000000則代表了extent的大小,我們每個extents是由8個塊組成的。

那在truncate之前,我們的高水位的塊是檔案5塊234,我們從塊128開始,每8個塊是一個extent,234是第14個extent的第三個塊。後面的6個塊是沒有插入資料的空塊。這個在前面我dump 最後一個L1塊得知。回顧一下,這裡下面的塊狀態顯示11111111 11000000。

wps388B.tmp

所以我們這個地方要把高水位從00000000 03000000 08000000 83004001修改成0d000000 02000000 08000000 ea004001。0d000000代表13,表明是第十四個擴充套件,02000000代表02,表明是第三個塊開始,而08000000還是一樣代表著這個擴充套件是8個塊的大小,而ea004001則代表著檔案5塊234。

wps388C.tmp

wps389D.tmp

wps389E.tmp

修改完成之後,重新整理buffer cache,然後重新查詢。

wps389F.tmp

3.Extents資訊恢復

可以看到資料量不對,這是因為我們Truncate之後,在段頭上只剩下了一個Extent的資訊。而我們的Extents是有14個的,這需要我們在修改如下幾個地方。Offset 264代表著我們Extents的數量,,這裡修改成0e代表了14個extents。

wps38B0.tmp

修改完Extents的數量之後,還需要新增對應的Extents Map的資訊。因為我們的Extents Map資訊也被刪除了。從我們的offset 280開始。

wps38B1.tmp

80004001 08000000這個Extents是我們第一個Extents。代表了檔案5的塊128。我們可以依次類推下列的資訊出來。而08000000則代表有8個資料塊構成一個Extents。

wps38C1.tmp

wps38C2.tmp

當然這些Extents Map修改完成之後,我們還需要在新增Auxillary Map。那什麼是輔助的Map呢?我們從Offset 2736開始。

wps38D3.tmp

這裡的80004001 83004001,代表這Extents中,L1塊的地址和Data Block的地址,可以看到在Extents 1上面,它的L1塊是128,而資料塊是從131開始的。因為我們的129是L2,130是段頭。所以下面我們構造其他的資料的時候,我們也要遵循這個規律。我們的Extents 2,它的L1還是128塊,但是它的資料塊確是從136開始的。而Extents 3,它的L1就是第二個L1塊,也就是144,而它的資料塊的開始則是從145開始的。依次類推下去。結果如下:

wps38D4.tmp

wps38E5.tmp

wps38E6.tmp

修改完成這些後,我們就能夠查到我們全部的資料了。

wps38E7.tmp

此時我們切勿執行一些其他的操作,應該儘快的使用CTAS的方式將這個表進行備份或者是匯出。因為段頭塊L2和L1的資訊還一些是沒有修改的。

wps38F7.tmp

至此,Truncate恢復完成。仔細的研究你才會發現,其實最重要的是摸清楚整個段的構造情況,只要你對整個段的構造情況,瞭若指掌,基本上恢復是很簡單的。

所以對於Truncate恢復,我總結以下步驟:

1. 遍歷所有資料檔案的資料塊,尋找offset 1是23的,23代表段頭塊,同時還要和相應的Data Object Id相同的,這個需要檢索offset 272的位置。

2. 找到了段頭塊,我們就可以通過offset 5192尋找到我們的L2塊。

3. 找到了L2塊,我們就可以通過offset 116找到所有的L1塊。找到了L1塊,我們就等於找到了資料塊。

4. 至此,我們就可以開始反向的構造段頭塊。

5. 修改段頭塊、L2塊和第一個L1塊的Data Object Id.,同時在修改資料字典。

6. 修改段頭塊的高水位資訊。當然這裡的高水位塊的辨別,一定會是在最後一個Extents上,你可以設定到最後一個Extnets的最後一個塊,這個資訊的準備性其實無所謂,全表掃描的時候它一定會掃描這個塊下面所有的塊。

7. 修改段頭上的Extents資訊。

以上步驟完成之後,資料就可以查到,首要步驟就是CTAS重建該物件。

About Me

....................................................................................................................................................

本文來自於微信公眾號轉載文章,若有侵權,請聯絡小麥苗及時刪除

ITPUB BLOG:http://blog.itpub.net/26736162

QQ:642808185 若加QQ請註明您所正在讀的文章標題

【版權所有,文章允許轉載,但須以連結方式註明源地址,否則追究法律責任】

....................................................................................................................................................

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

相關文章