Oracle資料庫特種恢復技術(一)—原理篇
Oracle資料庫特種恢復技術(一)—原理篇
作者:謝浩
oracle資料庫特種恢復技術(二)—塊內篇連結:http://www.itpub.net/thread-1507766-1-1.html
oracle資料庫特種恢復技術(三)—轉換篇連結:http://www.itpub.net/thread-1507774-1-1.html
Oracle資料庫特種恢復技術(四)—實驗篇連結:http://www.itpub.net/thread-1510202-1-1.html
oracle資料庫特種恢復技術(五)--redo篇連結:http://www.itpub.net/thread-1517926-1-1.html
由一次ora-00600 [4000]想到的:
專案測試庫出現過一次由於異常斷電引起的ora-00600 [4000]造成資料庫無法啟動的故障,診斷為系統回滾段資料字典(字典而不是回滾段本身)損壞,且該庫處於非歸檔模式,無任何備份。後使用bbed修改system表空間資料檔案,將相關資料塊的事物狀態手工修改為一致性狀態,資料庫得以開啟。
由此引發另一種思路:如果資料庫無法使用rman、冷備份、dg、ogg、dsg等方式正常恢復開啟,那麼還能否找回其中資料?考慮到業務資料還存在於儲存裝置上(只是完整性有一些缺失),能否在不啟動資料庫的情況下,由應用從儲存裝置上直接讀出資料庫中的業務資料?為解決以上問題,使用dba一些日常工具結合oracle資料庫內部結構的知識,來分析oracle資料庫定位資料的原理。這樣做的目的是最終能夠透過編寫程式碼(java或者c++)實現半自動或全自動的資料庫無法mount狀態下的恢復。
定位資料的方式:
Oracle資料庫服務程式在定位某行資料時,採用了資料字典+段頭extent map的“集中—分散”儲存模式,具體為:
1、在資料字典中存放資料段(對應表或分割槽)頭的物理位置
2、在資料段頭存放段中所有extent(即連續的block集合)的物理位置
首先從資料字典入手,oracle資料庫在建立時會生成一系列的基礎資料字典表,這些基礎字典記錄了一個資料庫的物理架構,oracle正是透過這些基礎字典,將邏輯層的表與物理層的儲存進行轉換和對映。其實建庫時執行的catalog.sql等指令碼只是在這些基礎資料字典上建立了相關的檢視、同義詞等(還有一些記憶體表的對映檢視),因此catalog.sql指令碼所建立的“字典”存在與否都不會影響資料庫的正常執行,但基礎資料字典一旦出現異常,資料庫就無法啟動。
oracle記錄段頭物理位置的字典檢視是sys.dba_segments,其中記錄了段頭所在的檔案號及在該檔案中的塊號,其中檔案號對應檔案物理位置在控制檔案中查詢。對於sys.dba_segments檢視,最值得關心或者說與本文關係最為密切的是:
select a.owner,a.segment_name,a.header_file,a.header_block fromdba_segments a;這四個欄位,透過這四個欄位就可以定位某使用者的某個段的段頭在哪個檔案的哪個資料塊內。透過plsql developer等工具檢視sys.dba_segments檢視的定義(sqlplus也可,但作為一個成熟的dba,熟練掌握各種工具的特點,完成不同任務,最大幅度減少達成目標所需的工作量,是一項非常重要的素質),可見其資料來源於sys.sys_dba_segs檢視:
create or replace view sys.dba_segments as
select owner, segment_name, partition_name,segment_type, tablespace_name,
header_file, header_block,
decode(bitand(segment_flags, 131072), 131072, blocks,
(decode(bitand(segment_flags,1),1,
dbms_space_admin.segment_number_blocks(tablespace_id, relative_fno,
header_block, segment_type_id, buffer_pool_id, segment_flags,
segment_objd, blocks), blocks)))*blocksize,
decode(bitand(segment_flags, 131072), 131072, blocks,
(decode(bitand(segment_flags,1),1,
dbms_space_admin.segment_number_blocks(tablespace_id, relative_fno,
header_block, segment_type_id, buffer_pool_id, segment_flags,
segment_objd, blocks), blocks))),
decode(bitand(segment_flags, 131072), 131072, extents,
(decode(bitand(segment_flags,1),1,
dbms_space_admin.segment_number_extents(tablespace_id, relative_fno,
header_block, segment_type_id, buffer_pool_id, segment_flags,
segment_objd, extents) , extents))),
initial_extent, next_extent, min_extents, max_extents, pct_increase,
freelists, freelist_groups, relative_fno,
decode(buffer_pool_id, 0, 'DEFAULT', 1, 'KEEP', 2, 'RECYCLE', NULL)
fromsys_dba_segs;
繼續觀察sys.sys_dba_segs檢視的定義,
create or replace view sys.sys_dba_segs
(owner, segment_name, partition_name,segment_type, segment_type_id, tablespace_id, tablespace_name, blocksize,header_file, header_block, bytes, blocks, extents, initial_extent, next_extent,min_extents, max_extents, pct_increase, freelists, freelist_groups, relative_fno,buffer_pool_id, segment_flags, segment_objd)
as
select NVL(u.name, 'SYS'), o.name, o.subname,
so.object_type, s.type#,
ts.ts#, ts.name, ts.blocksize,
f.file#, s.block#,
s.blocks * ts.blocksize, s.blocks, s.extents,
s.iniexts * ts.blocksize,
decode(bitand(ts.flags, 3), 1, to_number(NULL),
s.extsize* ts.blocksize),
s.minexts, s.maxexts,
decode(bitand(ts.flags, 3), 1, to_number(NULL),
s.extpct),
decode(bitand(ts.flags, 32), 32, to_number(NULL),
decode(s.lists, 0, 1, s.lists)),
decode(bitand(ts.flags, 32), 32, to_number(NULL),
decode(s.groups, 0, 1, s.groups)),
s.file#, s.cachehint, NVL(s.spare1,0), o.dataobj#
from sys.user$ u, sys.obj$ o, sys.ts$ ts,sys.sys_objects so, sys.seg$ s,
sys.file$ f
where s.file# = so.header_file
ands.block# = so.header_block
ands.ts# = so.ts_number
ands.ts# = ts.ts#
ando.obj# = so.object_id
ando.owner# = u.user# (+)
ands.type# = so.segment_type_id
ando.type# = so.object_type_id
ands.ts# = f.ts#
ands.file# = f.relfile#
union all
select NVL(u.name, 'SYS'), un.name, NULL,
decode(s.type#, 1, 'ROLLBACK', 10, 'TYPE2 UNDO'), s.type#,
ts.ts#, ts.name, ts.blocksize, f.file#, s.block#,
s.blocks * ts.blocksize, s.blocks, s.extents,
s.iniexts * ts.blocksize, s.extsize * ts.blocksize, s.minexts,
s.maxexts, s.extpct,
decode(bitand(ts.flags, 32), 32, to_number(NULL),
decode(s.lists, 0, 1, s.lists)),
decode(bitand(ts.flags, 32), 32, to_number(NULL),
decode(s.groups, 0, 1, s.groups)),
s.file#, s.cachehint, NVL(s.spare1,0), un.us#
from sys.user$ u, sys.ts$ ts, sys.undo$ un,sys.seg$ s, sys.file$ f
where s.file# = un.file#
ands.block# = un.block#
ands.ts# = un.ts#
ands.ts# = ts.ts#
ands.user# = u.user# (+)
ands.type# in (1, 10)
andun.status$ != 1
andun.ts# = f.ts#
andun.file# = f.relfile#
union all
select NVL(u.name, 'SYS'), to_char(f.file#) ||'.' || to_char(s.block#), NULL,
decode(s.type#, 2, 'DEFERRED ROLLBACK', 3, 'TEMPORARY',
4, 'CACHE', 9, 'SPACEHEADER', 'UNDEFINED'), s.type#,
ts.ts#, ts.name, ts.blocksize,
f.file#, s.block#,
s.blocks * ts.blocksize, s.blocks, s.extents,
s.iniexts * ts.blocksize,
decode(bitand(ts.flags, 3), 1, to_number(NULL),
s.extsize* ts.blocksize),
s.minexts, s.maxexts,
decode(bitand(ts.flags, 3), 1, to_number(NULL),
s.extpct),
decode(bitand(ts.flags, 32), 32, to_number(NULL),
decode(s.lists, 0, 1, s.lists)),
decode(bitand(ts.flags, 32), 32, to_number(NULL),
decode(s.groups, 0, 1, s.groups)),
s.file#, s.cachehint, NVL(s.spare1,0), s.hwmincr
from sys.user$ u, sys.ts$ ts, sys.seg$ s,sys.file$ f
where s.ts# = ts.ts#
ands.user# = u.user# (+)
ands.type# not in (1, 5, 6, 8, 10)
ands.ts# = f.ts#
and s.file# = f.relfile#;
可見其是由三個sql語句塊聯合而成,而其中2、3語句塊明顯是回滾段等segment,因此著重觀察第一語句塊,
select NVL(u.name, 'SYS'), o.name, o.subname,
so.object_type, s.type#,
ts.ts#, ts.name, ts.blocksize,
f.file#, s.block#,
s.blocks * ts.blocksize, s.blocks, s.extents,
s.iniexts * ts.blocksize,
decode(bitand(ts.flags, 3), 1, to_number(NULL),
s.extsize* ts.blocksize),
s.minexts, s.maxexts,
decode(bitand(ts.flags, 3), 1, to_number(NULL),
s.extpct),
decode(bitand(ts.flags, 32), 32, to_number(NULL),
decode(s.lists, 0, 1, s.lists)),
decode(bitand(ts.flags, 32), 32, to_number(NULL),
decode(s.groups, 0, 1, s.groups)),
s.file#, s.cachehint, NVL(s.spare1,0), o.dataobj#
from sys.user$ u, sys.obj$ o, sys.ts$ ts,sys.sys_objects so, sys.seg$ s,
sys.file$ f
where s.file# = so.header_file
ands.block# = so.header_block
ands.ts# = so.ts_number
ands.ts# = ts.ts#
ando.obj# = so.object_id
ando.owner# = u.user# (+)
ands.type# = so.segment_type_id
ando.type# = so.object_type_id
ands.ts# = f.ts#
and s.file# = f.relfile#
可見其資料來源為sys.user$ u, sys.obj$ o, sys.ts$ ts, sys.sys_objects so, sys.seg$s,sys.file$ f這幾個基礎系統字典表(這些已經都是物理存在的表而不是檢視了),而其中最重要的owner、segment_name、header_file、header_block欄位,則分別來自sys.user$、sys.obj$、sys.seg$、sys.file$等字典表,也就是說要在不啟動資料庫的情況下定位到使用者資料,必須先定位以上幾個基本資料字典。那麼這幾個表的物理儲存位置又在哪裡呢?或者說在資料庫不啟動的情況下,如何獲得這四個表的物理儲存位置?要回答這個問題,先要了解一下oracle啟動的過程,oracle在啟動時會載入這些基礎資料字典表,而這些字典表的create語句都寫在sys.bootstrap$表裡,
SQL> select *from sys.bootstrap$ a where a.sql_text like '%SEG$%';
LINE# OBJ# SQL_TEXT
---------- ------------------------------------------------------------------------------------------
14 14 CREATE TABLE SEG$("FILE#"NUMBER NOT NULL,"BLOCK#" NUMBER NOT NULL,"TYPE#" NUMBE
而sys.bootstrap$表的物理位置其實是間接“寫死”在oracle程式碼中的。
在每一個oracle資料庫(以10g為例,其他版本相差不大)的system表空間的第一個檔案的header block中都按固定結構存放有kcvfh這個結構體,其定義可以透過bbed工具檢視。
這裡簡單介紹一下bbed工具。Bbed是oracle公司提供的一個文字編輯工具(有些dba把bbed想象的非常神秘高深,其實它就是一個普通的文字編輯器,用其他文字編輯工具(比如UltraEdit)的宏功能其實可以實現bbed的絕大部分功能),透過它可以方便的檢視oracle資料在物理層面的儲存結構。
為安全考慮,先把資料庫system表空間的第一個檔案複製到另外的目錄以便觀察。在sqlplus中執行select file#||' '||name||' '||bytes from v$datafile ;將結果中的路徑和檔名修改為先前資料檔案的複製,用來生成bbed列表檔案,這樣用bbed開啟的就是這份複製檔案,而不是真正的庫中的檔案。Bbed安裝編譯過程不再贅述。
進入bbed後預設偏移量是1,即file header block,
BBED> map
File:/home/oracle/vie.pdf (1)
Block: 1 Dba:0x00400001
------------------------------------------------------------
DataFile Header
struct kcvfh, 676 bytes @0
ub4tailchk @8188
Map命令會自動檢測當前資料塊的type,並顯示該型別資料塊內所包含的資料結構資訊(這也就是為什麼設定不同偏移量,map輸出的資料結構資訊也會不同),這裡看到,system表空間的file header block中包含一個名為kcvfh的結構體,該結構體佔用676 bytes,深入觀察:
BBED> p kcvfh
struct kcvfh, 676 bytes @0
struct kcvfhbfh, 20 bytes @0
ub1type_kcbh @0 0x0b
ub1 frmt_kcbh @1 0xa2
ub1 spare1_kcbh @2 0x00
ub1 spare2_kcbh @3 0x00
ub4 rdba_kcbh @4 0x00400001
ub4 bas_kcbh @8 0x00000000
ub2 wrp_kcbh @12 0x0000
ub1 seq_kcbh @14 0x01
ub1 flg_kcbh @15 0x04 (KCBHFCKV)
ub2 chkval_kcbh @16 0x2671
ub2 spare3_kcbh @18 0x0000
struct kcvfhhdr, 76 bytes @20
ub4 kccfhswv @20 0x00000000
ub4 kccfhcvn @24 0x0a200100
ub4 kccfhdbi @28 0xea382478
text kccfhdbn[0] @32 P
text kccfhdbn[1] @33 H
text kccfhdbn[2] @34 O
text kccfhdbn[3] @35 N
text kccfhdbn[4] @36 E
text kccfhdbn[5] @37 D
text kccfhdbn[6] @38 B
text kccfhdbn[7] @39
ub4 kccfhcsq @40 0x00001ae3
ub4 kccfhfsz @44 0x00017c00
s_blkz kccfhbsz @48 0x00
ub2 kccfhfno @52 0x0001
ub2 kccfhtyp @54 0x0003
ub4 kccfhacid @56 0x00000000
ub4 kccfhcks @60 0x00000000
text kccfhtag[0] @64
text kccfhtag[1] @65
text kccfhtag[2] @66
text kccfhtag[3] @67
text kccfhtag[4] @68
text kccfhtag[5] @69
text kccfhtag[6] @70
text kccfhtag[7] @71
text kccfhtag[8] @72
text kccfhtag[9] @73
text kccfhtag[10] @74
text kccfhtag[11] @75
text kccfhtag[12] @76
text kccfhtag[13] @77
text kccfhtag[14] @78
text kccfhtag[15] @79
text kccfhtag[16] @80
text kccfhtag[17] @81
text kccfhtag[18] @82
text kccfhtag[19] @83
text kccfhtag[20] @84
text kccfhtag[21] @85
text kccfhtag[22] @86
text kccfhtag[23] @87
text kccfhtag[24] @88
text kccfhtag[25] @89
text kccfhtag[26] @90
text kccfhtag[27] @91
text kccfhtag[28] @92
text kccfhtag[29] @93
text kccfhtag[30] @94
text kccfhtag[31] @95
ub4kcvfhrdb @96 0x00400179
struct kcvfhcrs, 8 bytes @100
ub4 kscnbas @100 0x00000008
。。。。。。。。。。。。。。。。。
省略後面內容
這裡的 ub4 kcvfhrdb @96 0x00400179就是oracle啟動過程中非常關鍵的變數:root dba,root dba中記錄了sys.bootstrap$的物理位置,因此可以把root dba理解為指向sys.bootstrap$的指標。這裡該變數的值為0x00400179,是按照dba(datablock address)的格式存放,將其轉換為二進位制後的前10位為檔案號,後22位為塊號 0x00400179轉換為二進位制0000 0000 0100 0000 0000 0001 0111 1001,檔案號部分:0000 0000 01,塊號部分:00 0000 0000 0001 01111001,表示1號檔案的377號塊,這個數字不是我的庫的巧合,而是10g版本資料庫的固定位置,在oracle 10g資料庫啟動時會預設在該偏移量的位置尋找指向sys.bootstrap$表的指標。在11g中sys.bootstrap$的位置改到了0x00400208。而10g、11g該指標的位置(即rootdba變數自己的位置)是相同的,都是file header block的96偏移量位置。需要注意的是這裡的偏移量96,並不是從檔案頭部開始計算:oracle在建立datafile時,實際生成的檔案會比管理員輸入的檔案尺寸大一個db_block_size的大小,比如某資料庫的db_block_size=8k,建立表空間時指定某檔案大小為100M,那麼該檔案的實際大小是100M+8K,這8K放在檔案的最前面,稱作os header block,root dba(也就是指向sys.bootstrap$的指標)的物理偏移量是db_block_size+96。這也就是為什麼在一些使用裸裝置的庫上,在把裸裝置交給oracle的時候,不能把檔案尺寸寫的和裝置的尺寸完全一樣,而要稍小一點,否則建立時會出現ORA-27042錯誤。
這裡有一個小技巧,在使用bbed分析資料檔案結構時,要觀察在不同主機上的不同版本的庫,有些庫的資料檔案很大,複製、檢視不便,可以針對需要,使用dd命令擷取重點關注的部分,比如我在觀察11g的file header block時使用 ddif=/u01/app/oracle/oradata/mydb/system01.dbf f=/home/oracle/lt140_header2.tracecount=1000 skip=0 bs=8192擷取該檔案的前8M資料,生成一個檔案,然後傳輸到主要用來做分析工作的主機上,修改bbed配置檔案指向該檔案就能用bbed對其進行分析,這麼做還有一個原因就是11g是不帶bbed工具的,需要從10g複製原始檔再進行編譯連結。結果導向、儘量簡化中間操作過程是dba工作的重要指導原則。
透過root dba的瞭解,已經可以在不開啟資料庫的情況下定位到sys.bootstrap$表的物理位置,下一個問題是,sys.bootstrap$表裡儲存的只是基礎字典表的建立語句,那麼這些表的物理儲存位置是如何確定的?進一步觀察sys.bootstrap$表的sql_text欄位,以file$為例:SQL> set line 2000
SQL> set headoff
SQL> select *from sys.bootstrap$ a where a.obj# = 17;
17 17
CREATE TABLEFILE$("FILE#" NUMBER NOT NULL,"STATUS$" NUMBER NOT NULL,"BLOCKS"NUMBER NOT NULL,"TS#" NUMBER,"RELFILE#"NUMBER,"MAXEXTEND" NUMBER,"INC" NUMBER,"CRSCNWRP"NUMBER,"CRSCNBAS" NUMBER,"OWNERINSTANCE"VARCHAR2(30),"SPARE1" NUMBER,"SPARE2"NUMBER,"SPARE3" VARCHAR2(1000),"SPARE4" DATE) PCTFREE 10PCTUSED 40 INITRANS 1 MAXTRANS 255 STORAGE ( INITIAL 64K NEXT 1024K MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0OBJNO 17 EXTENTS (FILE 1 BLOCK 113))
這個create語句的storage子句中有兩個不常見的引數OBJNO 17 和EXTENTS (FILE 1 BLOCK 113),前者是指定該segment的物件號,而extents是直接指定segment的segmentheader block儲存位置,我多次嘗試在客戶發起的sql語句中使用這兩個引數未能成功,判斷為只有在資料庫處於特定狀態或以特定身份執行時才能使用這些引數。就是說這兩個引數直接指定了段在儲存上得物理位置。從該引數已經可以直接定位其所對應的資料字典段頭的物理位置,而oracle的段頭中的extents map結構體就包涵了該段全部的資料塊的實體地址。
在sys.bootstrap$表中記錄的資料字典表的create語句,還有一部分是沒有直接指定儲存位置的,這些表都是某個簇的成員,其實際資料儲存在簇中,比如SYS.TAB$:
CREATE TABLE TAB$("OBJ#" NUMBERNOT NULL,"DATAOBJ#" NUMBER,"TS#" NUMBER NOTNULL,"FILE#"
。。。。。。。。。。。。。。。。。。。。省略。。。。。。。。。。。。。。。。。。。。。。。。。。。。
NUMBER,"SPARE2"NUMBER,"SPARE3" NUMBER,"SPARE4"VARCHAR2(1000),"SPARE5" VARCHAR2(1000),"SPARE6" DATE)STORAGE ( OBJNO 4 TABNO 1) CLUSTERC_OBJ#(OBJ#)
紅色字型白色背景的就是指定SYS.TAB$表加入到C_OBJ#簇中。
至此,在關閉資料庫的情況下定位資料塊的大致思路已經清晰,總結如下:
1、在system 表空間的第一個資料檔案的特定偏移位置,找到root dba變數
2、root dba變數的值就是指向sys.bootstrap$表的物理位置的指標
3、sys.bootstrap$表中記錄了資料庫基礎字典表的物理位置
4、基礎字典表內記錄了使用者段段頭的物理儲存位置
下一步就是要透過某個段的segmentheader block從資料檔案中直接讀出表的資料。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/9606200/viewspace-745677/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 資料庫備份與恢復技術資料庫
- 【資料庫資料恢復】如何恢復Oracle資料庫truncate表的資料資料庫資料恢復Oracle
- 【資料庫資料恢復】誤truncate table的Oracle資料庫資料恢復方案資料庫資料恢復Oracle
- 【資料庫資料恢復】oracle資料庫誤truncate table怎麼恢復資料?資料庫資料恢復Oracle
- 【資料庫資料恢復】Oracle資料庫誤truncate table的資料恢復案例資料庫資料恢復Oracle
- 資料庫資料恢復-ORACLE資料庫的常見故障&各種故障下的資料恢復可能性資料庫資料恢復Oracle
- Oracle資料庫恢復之resetlogsOracle資料庫
- 【資料庫資料恢復】Oracle資料庫ASM磁碟組掉線如何恢復資料?資料庫資料恢復OracleASM
- 【資料庫資料恢復】ASM磁碟組掉線的Oracle資料庫資料恢復案例資料庫資料恢復ASMOracle
- Oracle & MySQL & PostgreSQL資料庫恢復支援OracleMySql資料庫
- Oracle資料庫冷備和恢復Oracle資料庫
- 資料庫資料恢復-oracle資料庫報錯無法開啟的如何恢復資料?資料庫資料恢復Oracle
- 【資料庫資料恢復】ASM例項不能掛載的Oracle資料庫資料恢復案例資料庫資料恢復ASMOracle
- 【資料庫資料恢復】LINUX環境下ORACLE資料庫誤刪除的資料恢復資料庫資料恢復LinuxOracle
- 【資料庫資料恢復】斷電導致Oracle資料庫資料丟失的資料恢復案例資料庫資料恢復Oracle
- [Oracle]Oracle資料庫資料被修改或者刪除恢復資料Oracle資料庫
- 【資料庫資料恢復】ORACLE常見資料災難&資料恢復可能性資料庫資料恢復Oracle
- 【北亞資料恢復】異常斷電導致Oracle資料庫報錯的oracle資料恢復資料恢復Oracle資料庫
- 【資料庫資料恢復】HP-UX系統ORACLE資料庫被誤刪除的資料恢復資料庫資料恢復UXOracle
- 【資料庫資料恢復】Oracle資料庫檔案出現壞塊報錯的資料恢復案例資料庫資料恢復Oracle
- 【oracle資料庫資料恢復】誤操作導致的資料庫誤刪除的資料恢復案例Oracle資料庫資料恢復
- 【資料庫資料恢復】SAP資料庫資料恢復案例資料庫資料恢復
- 寶塔資料庫恢復 mysql資料庫丟失恢復 mysql資料庫刪除庫恢復 寶塔mysql資料庫恢復資料庫MySql
- 【資料庫資料恢復】Oracle ASM例項無法掛載的資料恢復案例資料庫資料恢復OracleASM
- 【北亞資料恢復】oracle資料庫執行truncate table命令怎麼恢復資料?資料恢復Oracle資料庫
- 【資料庫資料恢復】Sql Server資料庫資料恢復案例資料庫資料恢復SQLServer
- 資料庫恢復子系統的常見技術和方案對比(一)資料庫
- 【北亞資料恢復】通過碎片拼接技術恢復XenServer伺服器磁碟中SQL Server資料庫資料資料恢復Server伺服器SQL資料庫
- 【資料庫資料恢復】透過恢復NDF檔案修復資料庫的資料恢復過程資料庫資料恢復
- 資料庫修復資料恢復資料庫資料恢復
- 恢復Oracle資料庫誤刪除資料的語句Oracle資料庫
- 【資料庫資料恢復】windows server下SqlServer資料庫的資料恢復資料庫資料恢復WindowsServerSQL
- 記一次 oracle 資料庫在當機後的恢復Oracle資料庫
- Oracle恢復誤刪資料Oracle
- 幾種主要的oracle資料庫問題發生後資料恢復的成功概率分析Oracle資料庫資料恢復
- Oracle、NoSQL和NewSQL 資料庫技術對比(一)OracleSQL資料庫
- 伺服器資料恢復-ext3檔案系統下oracle資料庫資料恢復案例伺服器資料恢復Oracle資料庫
- mysql資料庫恢復一例MySql資料庫
- 【資料庫資料恢復】linux系統下MYSQL資料庫資料恢復案例資料庫資料恢復LinuxMySql