(一) 问题背景
最近在生产环境中, 开发人员误操作, 使用 truncate 将 oracle 数据库某个表的数据全部删除了, 在删除之后, 开发人员发现自己闯祸了, 于是联系值班的 DBA 进行紧急数据恢复.
经过分析, 表被 truncate 后, 使用一般的闪回表, 闪回查询, 闪回事物等方法, 是不可能将数据找回来的, 可以使用闪回数据库, 闪回数据归档的方法来进行恢复, 但是通常在生产环境中, 都不会开启这 2 个特性, 所以剩下的只有使用 RMAN 进行数据恢复了.
对于使用 RMAN 进行数据恢复, 可以在生产环境上直接进行, 也可以恢复到其它机器上.
直接在生产环境上恢复:1需要停止生产数据库;2数据库需要保持一致性, 比如说, 我需要将数据库恢复到 12:00, 那么数据库中其他表的数据也将恢复到 12 点, 有可能会丢失较多数据;3如果恢复过程中出现其它问题也比较麻烦, 耽误了生产业务执行.
恢复到其它机器上:2不需要停生产库;2仅仅丢失 truncate 表的数据, 比如说, 我需要将数据库恢复到 12:00, 那么我只需将整个库在测试环境上恢复到 12 点, 再将我们丢失表的数据通过 DB_LINK 或数据泵等方式恢复到生产环境, 生产环境其它表的数据是不受影响的;3恢复失败, 并不会影响到生产库.
所以, 经过一番考虑, 决定将数据库恢复到其它机器上, 然后再将 truncate 表的数据导回到生产环境.
此次恢复操作是同事做的, 在恢复过程中, 由于流程不熟悉, 查资料耽误了一些时间 (大约 20 分钟), 虽然数据库恢复完成了, 但没有达到快速恢复的要求. 思考了一下, 假如自己来做, 能否在开发人员焦急等待的情况下, 自己毫不慌乱, 快速稳定的完成数据库恢复? 确实是不可能的. 一方面恢复流程不熟练, 毕竟数据库恢复操作一年也不可能遇到几次, 另一方面在用户及开发人员催促的情况下, DBA 也很容易慌张, 影响效率. 因此最好的方式是: 提前演练, 写好操作流程. 当故障发生时, 照着文档操作, 以最快的速度恢复生产.
(二) 环境准备
生产环境 | 异机环境 | |
操作系统 | RedHat6.7 | RedHat6.7 |
数据库版本 | 11.2.0.4(RAC,2 个节点) | 11.2.0.4(单节点) |
db_name | prodb | prodb |
instance_name | prodb1、prodb2 | prodb |
数据库安装情况 | 安装 GI + 数据库软件 + 创建数据库 | 安装 GI + 数据库软件 (不用创建数据库) |
磁盘组信息 | OCR : 3*1GB,normal DATA :3*5GB,external ARCH : 1*5GB,external | OCR : 3*1GB,normal DATA :3*5GB,external ARCH : 1*5GB,external |
备注: 为了方便, 在后续环境中, 生产环境数据库简称 "生产库", 异机环境的数据库简称 "测试库".
(三) 测试方案
(四) 详细执行过程
(4.1) 创建测试表
这里创建了 2 个测试表, 作用分别如下:
lijiaman.test01: 用于做 truncate 测试的表, 最后在测试库需要进行 test01 表的恢复.
lijiaman.test02: 用于模拟数据库事务, 对该表不断执行插入操作, 使得数据库产生大量归档日志.
(Ⅰ) 表 test01, 一共有 14 笔数据.
- SQL> CREATE TABLE test01 AS SELECT * FROM scott.emp;
- Table created
- SQL> select count(*) from test01;
- COUNT(*)
- ----------
- 14
(ⅠⅠ) 表 test02, 持续往里面写入数据
-- 创建表 test02
- create table test02
- (
- col1 number,
- col2 number,
- col3 varchar2(30),
- col4 date,
- col5 varchar2(100)
- );
-- 创建随机数据插入存储过程
- create or replace procedure p_insert_test02 is
- BEGIN
- FOR i IN 1..10000 LOOP
- insert into test02(col1,col2,col3,col4,col5)
- values
- ((select round(dbms_random.value(1, 100000000)) from dual),
- (select round(dbms_random.value(10000, 100000000)) from dual),
- (select dbms_random.string('a', 25) from dual),
- sysdate,
- (select dbms_random.string('a', 85) from dual));
- commit;
- END LOOP;
- end p_insert_test02;
-- 制定 job, 没隔 30s 执行一次上面的存储过程
- declare
- job1 number;
- begin
- sys.dbms_job.submit(job => job1,
- what => 'p_insert_test02;',
- next_date => sysdate,
interval => 'sysdate + 30/(1440*60)'); -- 每隔 30s 向 test02 表插入 10000 笔随机数据
commit;
end;
/
(4.2) 对数据库进行完全备份
- rman target /
- RMAN> run {
- allocate channel c1 type disk;
- allocate channel c2 type disk;
- sql'alter system archive log current';
- backup database format '/databaseBackup/full_db_%U';
- sql'alter system archive log current';
- backup archivelog all format '/databaseBackup/archlog_%U';
- backup current controlfile format '/databaseBackup/controlfile_%U';
- backup spfile format '/databaseBackup/spfile_%U';
- release channel c1;
- release channel c2;
- }
生成的备份集如下:
- [oracle@node1 databaseBackup]$ ls -l
- total 4136752
- -rw-r----- 1 oracle asmadmin 1451128832 Sep 27 19:27 archlog_0iucr7hg_1_1
- -rw-r----- 1 oracle asmadmin 1462116352 Sep 27 19:27 archlog_0jucr7hh_1_1
- -rw-r----- 1 oracle asmadmin 1406464 Sep 27 19:27 archlog_0kucr7lr_1_1
- -rw-r----- 1 oracle asmadmin 18841600 Sep 27 19:28 controlfile_0lucr7m2_1_1
- -rw-r----- 1 oracle asmadmin 805953536 Sep 27 19:25 full_db_0eucr7f7_1_1
- -rw-r----- 1 oracle asmadmin 477528064 Sep 27 19:25 full_db_0fucr7f7_1_1
- -rw-r----- 1 oracle asmadmin 18841600 Sep 27 19:25 full_db_0gucr7h3_1_1
- -rw-r----- 1 oracle asmadmin 98304 Sep 27 19:25 full_db_0hucr7ha_1_1
- -rw-r----- 1 oracle asmadmin 98304 Sep 27 19:28 spfile_0mucr7m5_1_1
确认归档日志备份情况, 可以看到, 本次全备份归档日志备份到了 thread1:57,thread2:48.
- RMAN> list archivelog all;
- List of Archived Log Copies for database with db_unique_name PRODB
- =====================================================================
- Key Thrd Seq S Low Time
- ------- ---- ------- - ---------
- 3 1 6 A 24-SEP-19
- Name: +ARCH/prodb/archivelog/2019_09_24/thread_1_seq_6.258.1019832847
- ......
- 100 1 57 A 27-SEP-19
- Name: +ARCH/prodb/archivelog/2019_09_27/thread_1_seq_57.355.1020108489
- 1 2 1 A 24-SEP-19
- Name: +ARCH/prodb/archivelog/2019_09_24/thread_2_seq_1.256.1019830885
- ......
- 80 2 48 A 24-SEP-19
- Name: +ARCH/prodb/archivelog/2019_09_24/thread_2_seq_48.335.1019838555
(4.3) 数据库正常运行, 产生大量归档
由于日志序列号是递增的 (以 resetlogs 打开数据库例外), 因此查询每个实例上生成的最大日志即可
- SELECT *
- FROM
- (SELECT thread#,
- SEQUENCE#,
- NAME,
- ROW_NUMBER() OVER(PARTITION BY thread# ORDER BY SEQUENCE# DESC) rn
- FROM V$ARCHIVED_LOG)
- WHERE rn=1;
结果为:
即 thread1 上的归档日志最大序列号为 67,thread2 上的归档日志最大序列号为 48(这里因为个人电脑开较多虚拟机太卡, 所以只开了节点 1, 节点 2 就没有日志生成, 并不影响本次实验结果的准确性).
(4.4) 模拟 test01 表被 truncate, 记下时间
- SQL> select sysdate from dual;
- SYSDATE
- -------------------
- 2019-09-27 19:37:31
- SQL>
- SQL> truncate table test01;
- Table truncated.
(4.5) 数据库正常运行, 产生大量归档
上一次备份到了 sequence=57 的日志, 上一次备份后又生成了 25 个日志, 这里生成大量日志是为了模拟生产数据库这次交易的情况.
(4.6) 开发人员发现表数据被 truncate
开发人员发现程序报错, 查看表 test01, 发现数据全没了, 开发人员确认数据被自己删除 (假设).
(4.7)DBA 执行异机恢复
思路整理:
本次恢复, 需要将 test01 表恢复到 truncate 之前, 我们需要有执行 truncate 操作之前的数据库全备和归档备份. 第一次全备归档日志文件之备份到了 thread1=57,thread2=48, 在执行全被之后, 又生成了许多的日志文件, 我们要将数据库恢复到 truncate 之前 (这里以我们记录的时间 2019-09-27 19:37:31 为恢复点), 那么我们还需要新的日志来做恢复, 需要的日志如下:
thread1: 日志 57~67 肯定需要, 日志 67~82 不一定需要;
thread2: 由于节点未开启, 不需要日志来做恢复.
step1: 将生产库的备份集传到测试库
[oracle@node1 databaseBackup]$ scp * 192.168.10.66:/databaseBackup/
step2: 对恢复需要的归档日志进行再次备份, 得到缺少的归档日志
- run {
- allocate channel c1 type disk;
- sql'alter system archive log current';
- backup archivelog all format '/databaseBackup/archlog_20190927_%U';
- release channel c1;
- }
得到的归档日志备份集如下:
- -rw-r----- 1 oracle asmadmin 1621476864 Sep 27 20:50 archlog_20190927_0nucrcd2_1_1
- -rw-r----- 1 oracle asmadmin 1643560960 Sep 27 20:51 archlog_20190927_0oucrcg5_1_1
- -rw-r----- 1 oracle asmadmin 1581030912 Sep 27 20:53 archlog_20190927_0pucrcjj_1_1
传送到备库上
[oracle@node1 databaseBackup]$ scp archlog_20190927_0* 192.168.10.66:/databaseBackup/
step3: 根据生产库的 pfile, 构造一个测试库的 pfile
- [oracle@test dbs]$ pwd
- /u01/App/oracle/product/11.2.0/db_1/dbs
- [oracle@test dbs]$ VIM init initprodb.ora
- # 添加如下信息
- audit_file_dest='/u01/app/oracle/admin/prodb/adump'
- audit_trail='db'
- compatible='11.2.0.4.0'
- control_files='+DATA/prodb/controlfile/current.260.1019830577'
- db_block_size=8192
- db_create_file_dest='+DATA'
- db_domain=''db_name='prodb'diagnostic_dest='/u01/app/oracle'dispatchers='(PROTOCOL=TCP) (SERVICE=prodbXDB)'
- enable_ddl_logging=TRUE
- log_archive_dest_1='LOCATION=+arch'
- log_archive_format='%t_%s_%r.dbf'
- open_cursors=300
- pga_aggregate_target=399507456
- processes=200
- remote_login_passwordfile='exclusive'
- sessions=225
- sga_target=1199570944
- prodb.undo_tablespace='UNDOTBS1'
- [oracle@test dbs]$ ls
- hc_prodb.dat hc_testdb1.dat hc_testdb.dat init.ora initprodb.ora lkTESTDB
创建 pfile 里面涉及到的路径:
[oracle@test ~]$ mkdir -p /u01/App/oracle/admin/prodb/adump
step4: 将备库启动到 nomount 状态
- [oracle@test ~]$ export ORACLE_SID=prodb
- [oracle@test ~]$ sqlplus / as sysdba
- SQL*Plus: Release 11.2.0.4.0 Production on Fri Sep 27 20:58:15 2019
- Copyright (c) 1982, 2013, Oracle. All rights reserved.
- Connected to an idle instance.
- SQL> startup nomount;
- ORACLE instance started.
- Total System Global Area 1202556928 bytes
- Fixed Size 2252704 bytes
- Variable Size 402653280 bytes
- Database Buffers 788529152 bytes
- Redo Buffers 9121792 bytes
- SQL>
step5: 将数据库添加到 HA 中, 以便可以使用 ASM 存储
[oracle@test ~]$ srvctl add database -d prodb -o /u01/App/oracle/product/11.2.0/db_1
step6: 恢复控制文件, 修改 pfile 文件, 重新启动数据库到 mount 状态
RMAN> restore controlfile from "/databaseBackup/controlfile_0lucr7m2_1_1";
注意: 此时存在一个问题, 我们在构造 pfile 文件的时候, 里面填写了控制文件的位置, 这个位置是生产库上的位置, 我们执行控制文件恢复后, 需要对参数文件中的 control_files 参数进行修改, 修改方法如下:
-- 首先, 确认 contril file 在 asm 中的位置,
- ASMCMD> pwd
- +data/prodb/controlfile
- ASMCMD> ls -lt
- Type Redund Striped Time Sys Name
- CONTROLFILE UNPROT FINE SEP 27 21:00:00 Y current.256.1020114329
-- 接下来, 修改 pfile 文件的 control_files 参数
- [oracle@test ~]$ cd $ORACLE_HOME/dbs
- [oracle@test dbs]$ VIM initprodb.ora
- # 改 control_files 位置
- control_files='+data/prodb/controlfile/current.256.1020114329'
-- 重启数据库到 mount 状态
- [oracle@test ~]$ sqlplus / as sysdba
- SQL*Plus: Release 11.2.0.4.0 Production on Fri Sep 27 21:17:26 2019
- Copyright (c) 1982, 2013, Oracle. All rights reserved.
- Connected to:
- Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
- With the Partitioning, Automatic Storage Management, OLAP, Data Mining
- and Real Application Testing options
- SQL> shutdown immediate
- ORA-01507: database not mounted
- ORACLE instance shut down.
- SQL> startup mount
- ORACLE instance started.
- Total System Global Area 1202556928 bytes
- Fixed Size 2252704 bytes
- Variable Size 402653280 bytes
- Database Buffers 788529152 bytes
- Redo Buffers 9121792 bytes
- Database mounted.
- SQL>
step7: 注册新的归档日志备份集到备库的控制文件中
- RMAN> catalog backuppiece "/databaseBackup/archlog_20190927_0nucrcd2_1_1";
- RMAN> catalog backuppiece "/databaseBackup/archlog_20190927_0oucrcg5_1_1";
- RMAN> catalog backuppiece "/databaseBackup/archlog_20190927_0pucrcjj_1_1";
step8: 恢复数据库到 truncate 之前
- RMAN>SQL"ALTER SESSION SET NLS_LANGUAGE=''AMERICAN''";
- RMAN>SQL"ALTER SESSION SET NLS_DATE_FORMAT=''YYYY-MM-DD HH24:MI:SS''";
- RUN{
- SET UNTIL TIME '2019-09-27 19:37:31';
- RESTORE DATABASE;
- RECOVER DATABASE;
- }
step9: 确认数据是已经否恢复回来
-- 先以只读方式打开数据库, 如果有问题, 还可以重新执行恢复
- SQL> alter database open read only;
- Database altered.
-- 确认数据是否找回来
- SQL> select count(*) from lijiaman.test01;
- COUNT(*)
- ----------
- 14
step10: 如果没问题, 关闭数据库, 以 resetlogs 方式打开
- SQL> shutdown immediate;
- Database closed.
- Database dismounted.
- ORACLE instance shut down.
- SQL> startup mount
- ORACLE instance started.
- Total System Global Area 1202556928 bytes
- Fixed Size 2252704 bytes
- Variable Size 402653280 bytes
- Database Buffers 788529152 bytes
- Redo Buffers 9121792 bytes
- Database mounted.
- SQL> alter database open resetlogs;
- Database altered.
恢复完成.
(4.8) 将恢复的数据导入到生产环境
可以使用 expdp/impdp 或者是 dblink 将数据从测试库导入到生产库中.
[完]
来源: https://www.cnblogs.com/lijiaman/p/11577001.html