Oracle中truncate删除的数据还能找到?
Oracle的truncate删除数据操作,不会写日志,不产生回滚段,因此执行速度快,相当于Windows的shift+del,不经回收站直接删除。
如果想找回truncate删除的数据,常规的可以采用这几种方案,
(1)使用数据泵导入。操作简单,但是前提必须要有备份可用,并且会有数据的丢失。(2)使用RMAN进行不完全恢复。前提是开启备份,可将数据库恢复到truncate之前的时刻,但是恢复时间较长。
还可以采用非常规的方案,例如通过odu、prm-dul、GDUL等收费软件进行恢复,还可以使用fy_recover_data包。
FY_Recover_Data是国内曾经的Oracle ACE大佬黄玮(个人网站:http://www.hellodba.com)开发的一个package,该脚本专门用于对truncate的表进行恢复。
作者讲述这个包的原理:如果我们已经有一套元数据及数据块,然后将被TRUNCATE的用户数据块的内容取代其用户数据块的内容,是否可以"骗"过Oracle,让它读出这些数据呢?
回顾一下表扫描的过程,这个方法应该是可行的。我们只要想办法构造出一个结构相同、且具有完整元数据信息和格式化了的用户数据块的傀儡表对象,然后将被truncate的用户数据块找出,再将其数据内容部分嫁接到傀儡对象的用户数据块,让Oracle认为这是傀儡对象的数据,就能让Oracle扫描并读出数据内容。
其原理用图示描述如下,
+-------------------------+ | Copy Of Dummy Data File | | (With Formmated Blocks)| +-------------------------+ || / (Blcok Header, Block Tail) || / +-------------------+ +----------------+ Table Scan +---------------+ | Source Data File | => (Data Block Content) => | Dummy Table | ============> | Restore Table | |(Without Meta Data)| |(With Meta Data)| +---------------+ +-------------------+ +----------------+
FY_Recover_Data对于表恢复的支持性如下,

通过使用FY_Recover_Data对truncate几种情况进行恢复测试,以验证fy_recover_data的恢复能力。
(1)使用fy_recover_data包执行truncate恢复,truncate后未有新数据进入表。
STEP1:创建测试表,并执行truncate
SQL> create table test01 as select * from dba_objects;SQL> select count(*) from test01; COUNT(*)---------- 86968SQL> truncate table test01;Table truncatedSQL> select count(*) from test01; COUNT(*)---------- 0
STEP2:导入FY_Recover_Data.pck包
[oracle@source-node ~]$ sqlplus as sysdbaSQL*Plus: Release 11.2.0.4.0 Production on Tue Apr 21 10:50:17 2020Copyright (c) 1982, 2013, Oracle. All rights reserved.Connected to:Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit ProductionWith the Partitioning, OLAP, Data Mining and Real Application Testing optionsSQL> @/home/oracle/FY_Recover_Data.pck -- 第一次执行发现第30行存在“&”符号,删除该符号Enter value for files: old 30: -- 1. Temp Restore and Recover tablespace & files ---new 30: -- 1. Temp Restore and Recover tablespace ---Package created.Warning: Package Body created with compilation errors.SQL> @/home/oracle/FY_Recover_Data.pck -- 删除“&”符号后导入成功Package created.Package body created.
STEP3:开始执行恢复,只需要两个参数:schema和table_name,
[oracle@source-node ~]$ sqlplus as sysdbaSQL*Plus: Release 11.2.0.4.0 Production on Tue Apr 21 11:11:20 2020Copyright (c) 1982, 2013, Oracle. All rights reserved.Connected to:Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit ProductionWith the Partitioning, OLAP, Data Mining and Real Application Testing optionsSQL> set time on 11:11:43 SQL> set serveroutput on11:11:54 SQL> exec fy_recover_data.recover_truncated_table('TEST','TEST01');11:12:01: Use existing Directory Name: FY_DATA_DIR11:12:02: Recover Table: TEST.TEST01$11:12:02: Restore Table: TEST.TEST01$$11:12:09: Copy file of Recover Tablespace: FY_REC_DATA_COPY.DAT111:12:09: begin to recover table TEST.TEST0111:12:09: Use existing Directory Name: TMP_HF_DIR11:12:09: Recovering data in datafile/u01/app/oracle/oradata/testdb1/users01.dbf11:12:09: Use existing Directory Name: TMP_HF_DIR11:12:39: 1242 truncated data blocks found.11:12:39: 86968 records recovered in backup table TEST.TEST01$$11:12:39: Total: 1242 truncated data blocks found.11:12:39: Total: 86968 records recovered in backup table TEST.TEST01$$11:12:39: Recovery completed.11:12:39: Data has been recovered to TEST.TEST01$$PL/SQL procedure successfully completed.
STEP4:根据恢复日志,会创建临时中转表test01$和test01$$,恢复的数据保存在test01$$中,
SQL> show userUser is "TEST"SQL> select count(*) from test01$$; COUNT(*)---------- 86968--将数据还原到test01表中SQL> insert into test01 select * from test01$$;--确认数据已经还原回来SQL> select count(*) from test01; COUNT(*)---------- 86968
经过测试,如果表被truncate后,未执行其它操作,数据可以使用fy_recover_data恢复回来。
(2)使用fy_recover_data包执行truncate恢复,truncate后有新数据进入表(新插入的数据比truncate之前多)
STEP1:创建测试表、序列、存储过程
SQL> create table test01 2 ( 3 col1 number, 4 col2 number, 5 col3 date, 6 col4 varchar2(30), 7 col5 varchar2(100) 8 );Table createdSQL> --创建自增序列SQL> CREATE SEQUENCE seq01 2 START WITH 1 3 MAXVALUE 99999999 4 MINVALUE 0 5 CYCLE 6 CACHE 10 7 ORDER;Sequence created SQL> --创建随机数据插入存储过程,其中col1列单调递增create or replace procedure p_insert_test01 ISv_col1 NUMBER;BEGINFOR i IN 1..10000 LOOPselect seq01.nextval INTO v_col1 from dual;insert into test01(col1,col2,col3,col4,col5)values(v_col1,(select round(dbms_random.value(10000, 100000000)) from dual),sysdate,(select dbms_random.string('a', 25) from dual),(select dbms_random.string('a', 85) from dual));END LOOP;commit;end p_insert_test01;
STEP2:测试表插入10000条数据,col1列的值从1到10000,
SQL> exec p_insert_test01;PL/SQL procedure successfully completedSQL> select count(*) from test01; COUNT(*)---------- 10000 SQL> SELECT MIN(col1),MAX(col1) FROM test01;MIN(COL1) MAX(COL1)---------- ---------- 1 10000
STEP3:执行truncate操作,
SQL> truncate table test01;Table truncated
STEP4:接着往表里插入20000条数据,
SQL> exec p_insert_test01;PL/SQL procedure successfully completedSQL> exec p_insert_test01;PL/SQL procedure successfully completedSQL> select count(*) from test01; COUNT(*)---------- 20000 SQL> SELECT MIN(col1),MAX(col1) FROM test01;MIN(COL1) MAX(COL1)---------- ---------- 10001 30000
STEP5:执行恢复操作,
[oracle@source-node ~]$ sqlplus as sysdbaSQL*Plus: Release 11.2.0.4.0 Production on Tue Apr 21 14:00:57 2020Copyright (c) 1982, 2013, Oracle. All rights reserved.Connected to:Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit ProductionWith the Partitioning, OLAP, Data Mining and Real Application Testing optionsSQL> set serveroutput onSQL> set time on14:01:09 SQL> exec fy_recover_data.recover_truncated_table('TEST','TEST01');14:01:13: Use existing Directory Name: FY_DATA_DIR14:01:13: Recover Table: TEST.TEST01$14:01:14: Restore Table: TEST.TEST01$$14:01:18: Copy file of Recover Tablespace: FY_REC_DATA_COPY.DAT114:01:18: begin to recover table TEST.TEST0114:01:18: Use existing Directory Name: TMP_HF_DIR14:01:18: Recovering data in datafile/u01/app/oracle/oradata/testdb1/users01.dbf14:01:18: Use existing Directory Name: TMP_HF_DIR14:01:32: 402 truncated data blocks found.14:01:32: 20000 records recovered in backup table TEST.TEST01$$14:01:32: Total: 402 truncated data blocks found.14:01:32: Total: 20000 records recovered in backup table TEST.TEST01$$14:01:32: Recovery completed.14:01:32: Data has been recovered to TEST.TEST01$$PL/SQL procedure successfully completed.
STEP6:通过对test01$$表进行确认,发现返回的数据是truncate之后插入的数据,不符合要求。
SQL> select count(*) from test01; COUNT(*)---------- 20000SQL> select count(*) from test01$$; COUNT(*)---------- 20000SQL> SELECT MIN(col1),MAX(col1) FROM test01;MIN(COL1) MAX(COL1)---------- ---------- 10001 30000SQL> SELECT MIN(col1),MAX(col1) FROM test01$$;MIN(COL1) MAX(COL1)---------- ---------- 10001 30000
(3)使用fy_recover_data包执行truncate恢复,truncate后有新数据进入表(新插入的数据比truncate之前少)。
STEP1:创建测试表、序列、存储过程,
SQL> DROP TABLE test01 PURGE;Table droppedSQL> create table test01 2 ( 3 col1 number, 4 col2 number, 5 col3 date, 6 col4 varchar2(30), 7 col5 varchar2(100) 8 );Table createdSQL> DROP SEQUENCE seq01;Sequence droppedSQL> --创建自增序列SQL> CREATE SEQUENCE seq01 2 START WITH 1 3 MAXVALUE 99999999 4 MINVALUE 0 5 CYCLE 6 CACHE 10 7 ORDER;Sequence createdSQL> --创建随机数据插入存储过程,其中col1列单调递增SQL> create or replace procedure p_insert_test01 IS 2 v_col1 NUMBER; 3 BEGIN 4 FOR i IN 1..10000 LOOP 5 select seq01.nextval INTO v_col1 from dual; 6 insert into test01(col1,col2,col3,col4,col5) 7 values 8 (v_col1, 9 (select round(dbms_random.value(10000, 100000000)) from dual), 10 sysdate, 11 (select dbms_random.string('a', 25) from dual), 12 (select dbms_random.string('a', 85) from dual)); 13 END LOOP; 14 commit; 15 end p_insert_test01;16 /Procedure created
STEP2:测试表插入10000条数据,col1列的值从1到10000,
SQL> exec p_insert_test01;PL/SQL procedure successfully completedSQL> select count(*) from test01; COUNT(*)---------- 10000 SQL> SELECT MIN(col1),MAX(col1) FROM test01;MIN(COL1) MAX(COL1)---------- ---------- 1 10000
STEP3:执行truncate操作,
SQL> truncate table test01;Table truncated
STEP4:修改存储过程,酶促插入100条数据,
SQL> --创建随机数据插入存储过程,其中col1列单调递增SQL> create or replace procedure p_insert_test01 IS 2 v_col1 NUMBER; 3 BEGIN 4 FOR i IN 1..100 LOOP 5 select seq01.nextval INTO v_col1 from dual; 6 insert into test01(col1,col2,col3,col4,col5) 7 values 8 (v_col1, 9 (select round(dbms_random.value(10000, 100000000)) from dual),10 sysdate,11 (select dbms_random.string('a', 25) from dual),12 (select dbms_random.string('a', 85) from dual)); 13 END LOOP; 14 commit; 15 end p_insert_test01; 16 /Procedure created-- 测试表插入100条数据SQL> exec p_insert_test01;PL/SQL procedure successfully completed
STEP5:执行恢复操作,
[oracle@source-node ~]$ sqlplus as sysdbaSQL*Plus: Release 11.2.0.4.0 Production on Tue Apr 21 14:22:34 2020Copyright (c) 1982, 2013, Oracle. All rights reserved.Connected to:Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit ProductionWith the Partitioning, OLAP, Data Mining and Real Application Testing optionsSQL> set time on14:22:39 SQL> set serveroutput on14:22:44 SQL> exec fy_recover_data.recover_truncated_table('TEST','TEST01');14:22:52: Use existing Directory Name: FY_DATA_DIR14:22:52: Recover Table: TEST.TEST01$14:22:52: Restore Table: TEST.TEST01$$14:22:57: Copy file of Recover Tablespace: FY_REC_DATA_COPY.DAT114:22:57: begin to recover table TEST.TEST0114:22:57: Use existing Directory Name: TMP_HF_DIR14:22:57: Recovering data in datafile/u01/app/oracle/oradata/testdb1/users01.dbf14:22:57: Use existing Directory Name: TMP_HF_DIR14:23:06: 5 truncated data blocks found.14:23:06: 100 records recovered in backup table TEST.TEST01$$14:23:06: Total: 5 truncated data blocks found.14:23:06: Total: 100 records recovered in backup table TEST.TEST01$$14:23:06: Recovery completed.14:23:06: Data has been recovered to TEST.TEST01$$PL/SQL procedure successfully completed.
STEP6:通过对test01$$表进行确认,发现返回的数据是truncate之后插入的数据,不符合要求。
SQL> select count(*) from test01; COUNT(*)---------- 100SQL> SELECT MIN(col1),MAX(col1) FROM test01;MIN(COL1) MAX(COL1)---------- ---------- 10001 10100SQL> select count(*) from test01$$; COUNT(*)---------- 100SQL> SELECT MIN(col1),MAX(col1) FROM test01$$;MIN(COL1) MAX(COL1)---------- ---------- 10001 10100
(4)测试数据文件被覆盖是否影响恢复。
STEP1:创建测试表,
SQL> create table test01 2 ( 3 col1 number, 4 col2 number, 5 col3 date, 6 col4 varchar2(30), 7 col5 varchar2(100) 8 ) TABLESPACE USERS;Table created
STEP2:初始时候,表空间总共20MB,剩余15.94MB,
SQL> SELECT SUBSTR(a.TABLESPACE_NAME,1,30) TablespaceName, 2 round(SUM(a.bytes/1024/1024),2) AS "Totle_size(MB)", 3 round(SUM(NVL(b.free_space1/1024/1024,0)),2) AS "Free_space(MB)", 4 round(SUM(a.bytes/1024/1024),2)-round(SUM(NVL(b.free_space1/1024/1024,0)),2) AS "Used_space(MB)", 5 ROUND((SUM(a.bytes/1024/1024)-SUM(NVL(b.free_space1/1024/1024,0))) *100/SUM(a.bytes/1024/1024),2) AS "Used_percent%", 6 round(SUM((case when a.MAXBYTES = 0 then a.bytes else a.MAXBYTES end)/1024/1024),2) AS "Max_size(MB)", 7 ROUND((SUM(a.bytes/1024/1024)-SUM(NVL(b.free_space1/1024/1024,0)))*100/SUM((case when a.MAXBYTES = 0 then a.bytes else a.MAXBYTES end)/1024/1024),2) AS "Max_percent%" 8 FROM dba_data_files a, 9 (SELECT SUM(NVL(bytes,0)) free_space1, 10 file_id 11 FROM dba_free_space 12 GROUP BY file_id 13 ) b 14 WHERE a.file_id = b.file_id(+) 15 AND a.TABLESPACE_NAME = 'USERS' 16 GROUP BY a.TABLESPACE_NAME;TABLESPACENAME Totle_size(MB) Free_space(MB) Used_space(MB) Used_percent% Max_size(MB) Max_percent%------------------ -------------- -------------- -------------- ------------- ------------ ------------USERS 20 15.94 4.06 20.31 20 20.31
STEP3:test01表插入大量数据,
SQL> exec p_insert_test01;PL/SQL procedure successfully completedSQL> PL/SQL procedure successfully completedSQL> PL/SQL procedure successfully completedSQL> PL/SQL procedure successfully completedSQL> PL/SQL procedure successfully completedSQL> PL/SQL procedure successfully completedSQL> PL/SQL procedure successfully completedSQL> PL/SQL procedure successfully completedSQL> /PL/SQL procedure successfully completedSQL> /begin p_insert_test01; end;ORA-01653: unable to extend table TEST.TEST01 by 128 in tablespace USERSORA-06512: at "TEST.P_INSERT_TEST01", line 6ORA-06512: at line 1
STEP4:此时,表空间总共20MB,剩余0.94MB,
SQL> SELECT SUBSTR(a.TABLESPACE_NAME,1,30) TablespaceName, 2 round(SUM(a.bytes/1024/1024),2) AS "Totle_size(MB)", 3 round(SUM(NVL(b.free_space1/1024/1024,0)),2) AS "Free_space(MB)", 4 round(SUM(a.bytes/1024/1024),2)-round(SUM(NVL(b.free_space1/1024/1024,0)),2) AS "Used_space(MB)", 5 ROUND((SUM(a.bytes/1024/1024)-SUM(NVL(b.free_space1/1024/1024,0))) *100/SUM(a.bytes/1024/1024),2) AS "Used_percent%", 6 round(SUM((case when a.MAXBYTES = 0 then a.bytes else a.MAXBYTES end)/1024/1024),2) AS "Max_size(MB)", 7 ROUND((SUM(a.bytes/1024/1024)-SUM(NVL(b.free_space1/1024/1024,0)))*100/SUM((case when a.MAXBYTES = 0 then a.bytes else a.MAXBYTES end)/1024/1024),2) AS "Max_percent%" 8 FROM dba_data_files a, 9 (SELECT SUM(NVL(bytes,0)) free_space1, 10 file_id 11 FROM dba_free_space 12 GROUP BY file_id 13 ) b 14 WHERE a.file_id = b.file_id(+) 15 AND a.TABLESPACE_NAME = 'USERS' 16 GROUP BY a.TABLESPACE_NAME;TABLESPACENAME Totle_size(MB) Free_space(MB) Used_space(MB) Used_percent% Max_size(MB) Max_percent%----------------- -------------- -------------- -------------- ------------- ------------ ------------USERS 20 0.94 19.06 95.31 20 95.31
STEP5:此时test01表有90000行数据,
SQL> select count(*) from test01; COUNT(*)---------- 90000SQL> SELECT MIN(col1),MAX(col1) FROM test01;MIN(COL1) MAX(COL1)---------- ---------- 109751 199750
STEP6:对test01执行truncate,
SQL> truncate table test01;Table truncated
STEP7:执行truncate后,空间已经释放,
SQL> SELECT SUBSTR(a.TABLESPACE_NAME,1,30) TablespaceName, 2 round(SUM(a.bytes/1024/1024),2) AS "Totle_size(MB)", 3 round(SUM(NVL(b.free_space1/1024/1024,0)),2) AS "Free_space(MB)", 4 round(SUM(a.bytes/1024/1024),2)-round(SUM(NVL(b.free_space1/1024/1024,0)),2) AS "Used_space(MB)", 5 ROUND((SUM(a.bytes/1024/1024)-SUM(NVL(b.free_space1/1024/1024,0))) *100/SUM(a.bytes/1024/1024),2) AS "Used_percent%", 6 round(SUM((case when a.MAXBYTES = 0 then a.bytes else a.MAXBYTES end)/1024/1024),2) AS "Max_size(MB)", 7 ROUND((SUM(a.bytes/1024/1024)-SUM(NVL(b.free_space1/1024/1024,0)))*100/SUM((case when a.MAXBYTES = 0 then a.bytes else a.MAXBYTES end)/1024/1024),2) AS "Max_percent%" 8 FROM dba_data_files a, 9 (SELECT SUM(NVL(bytes,0)) free_space1, 10 file_id 11 FROM dba_free_space 12 GROUP BY file_id 13 ) b 14 WHERE a.file_id = b.file_id(+) 15 AND a.TABLESPACE_NAME = 'USERS' 16 GROUP BY a.TABLESPACE_NAME;TABLESPACENAME Totle_size(MB) Free_space(MB) Used_space(MB) Used_percent% Max_size(MB) Max_percent%----------------- -------------- -------------- -------------- ------------- ------------ ------------USERS 20 15.88 4.12 20.63 20 20.63
STEP8:创建表test02,用来覆盖test01释放的空间,
SQL> create table test02 as select * from dba_objects;Table created
STEP9:test02表创建之后,剩余空间为5.88MB,可以说明:test02表的数据占用了test01表释放出来的空间,即test01表的部分数据已经被覆盖。
SQL> SELECT SUBSTR(a.TABLESPACE_NAME,1,30) TablespaceName, 2 round(SUM(a.bytes/1024/1024),2) AS "Totle_size(MB)", 3 round(SUM(NVL(b.free_space1/1024/1024,0)),2) AS "Free_space(MB)", 4 round(SUM(a.bytes/1024/1024),2)-round(SUM(NVL(b.free_space1/1024/1024,0)),2) AS "Used_space(MB)", 5 ROUND((SUM(a.bytes/1024/1024)-SUM(NVL(b.free_space1/1024/1024,0))) *100/SUM(a.bytes/1024/1024),2) AS "Used_percent%", 6 round(SUM((case when a.MAXBYTES = 0 then a.bytes else a.MAXBYTES end)/1024/1024),2) AS "Max_size(MB)", 7 ROUND((SUM(a.bytes/1024/1024)-SUM(NVL(b.free_space1/1024/1024,0)))*100/SUM((case when a.MAXBYTES = 0 then a.bytes else a.MAXBYTES end)/1024/1024),2) AS "Max_percent%" 8 FROM dba_data_files a, 9 (SELECT SUM(NVL(bytes,0)) free_space1, 10 file_id 11 FROM dba_free_space 12 GROUP BY file_id 13 ) b 14 WHERE a.file_id = b.file_id(+) 15 AND a.TABLESPACE_NAME = 'USERS' 16 GROUP BY a.TABLESPACE_NAME;TABLESPACENAME Totle_size(MB) Free_space(MB) Used_space(MB) Used_percent% Max_size(MB) Max_percent%---------------- -------------- -------------- -------------- ------------- ------------ ------------USERS 20 5.88 14.12 70.63 20 70.63
STEP10:执行恢复操作,
[oracle@source-node ~]$ sqlplus / as sysdbaSQL*Plus: Release 11.2.0.4.0 Production on Tue Apr 21 15:09:58 2020Copyright (c) 1982, 2013, Oracle. All rights reserved.Connected to:Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit ProductionWith the Partitioning, OLAP, Data Mining and Real Application Testing optionsSQL> set time on15:10:05 SQL> set serveroutput on15:10:10 SQL> exec fy_recover_data.recover_truncated_table('LIJIAMAN','TEST01');15:10:17: Use existing Directory Name: FY_DATA_DIR15:10:17: Recover Table: LIJIAMAN.TEST01$15:10:17: Restore Table: LIJIAMAN.TEST01$$15:10:22: Copy file of Recover Tablespace: FY_REC_DATA_COPY.DAT115:10:22: begin to recover table LIJIAMAN.TEST0115:10:22: Use existing Directory Name: TMP_HF_DIR15:10:22: Recovering data in datafile/u01/app/oracle/oradata/testdb1/users01.dbf15:10:22: Use existing Directory Name: TMP_HF_DIR15:10:31: 645 truncated data blocks found.15:10:31: 24439 records recovered in backup table LIJIAMAN.TEST01$$15:10:31: Total: 645 truncated data blocks found.15:10:31: Total: 24439 records recovered in backup table LIJIAMAN.TEST01$$15:10:31: Recovery completed.15:10:31: Data has been recovered to LIJIAMAN.TEST01$$PL/SQL procedure successfully completed.
STEP11:发现只恢复了部分数据,不符合要求,
-- truncate之前test01表有90000行数据,恢复了24339行数据SQL> select count(*) from test01$$; COUNT(*)---------- 24439SQL> SELECT MIN(col1),MAX(col1) FROM test01$$;MIN(COL1) MAX(COL1)---------- ---------- 109751 199750
因此,使用工具fy_recover_data进行数据恢复,需要确认,
(1)truncate之后,需要保证没有新的数据进入表中,否则无法还原;
(2)存放该表的数据文件块不能被覆盖,否则无法完整还原数据。
在发生故障后,可以迅速使用,
SQL> alter tablespace users read only;SQL> alter tablespace users read write;
来关闭/开启表空间的写功能,这样可以保证数据文件不会被覆写。
参考,https://www.modb.pro/db/32403?utm_source=index_aihttps://www.cnblogs.com/lijiaman/p/12747658.html
如果您认为这篇文章有些帮助,还请不吝点下文章末尾的"点赞"和"在看",或者直接转发pyq,

近期更新的文章:《MySQL创建内部临时表的场景》《我国法律位阶的学习和了解》《金融知识小科普 - 多层次资本市场》《如何定位锁定用户的元凶?》
《数据库干货硬核公众号》
近期的热文:《推荐一篇Oracle RAC Cache Fusion的经典论文》
《"红警"游戏开源代码带给我们的震撼》
文章分类和索引:《公众号1300篇文章分类和索引》