一、背景
在日常运维过程中,如果不小心使用高危命令删除了数据库的数据,如果没有数据备份,记住不要跑路噢,跟着我一起学习如何恢复数据。
二、概念普及
UNDO表的作用
Undo 表存储了已提交和未提交事务所产生的旧数据版本,在需要回滚事务或实现并发控制时使用。每当对数据库进行DML操作时(例如插入、更新或删除记录),Oracle 就会在 undo 表中保存之前数据的副本,在需要的时候回滚。
事务回滚: 当事务需要回滚时,Oracle 可以使用 undo 表中的数据来还原到事务开始之前的状态,确保数据的一致性和完整性。
并发控制: 在并发访问数据库时,多个事务可能同时修改相同的数据。Undo 表允许数据库保持一致性,确保每个事务在读取和修改数据时都能看到正确的数据版本。
读一致性: 当一个事务正在修改数据时,其他事务可能需要读取一致性的数据。Undo 表使得其他事务可以读取在当前事务修改之前的数据版本,从而实现读一致性。
闪回查询: 使用闪回查询功能,可以利用 undo 表中的数据来恢复数据库中的特定时间点的数据状态,而无需从备份进行还原。
dml产生的undo的量:
在dml操作提交前,能够进行回滚操作,回滚信息就是从undo段中读取的。
回滚等效于反向操作。undo不会记录语句,记录的是数据的变化。
insert产生undo 仅仅记录rowid
delete 产生undo, 记录改行所有信息
update 产生undo,改动前后的字段。
三、场景模拟
今天来模拟使用delete误删数据后采用使用闪回查询恢复表到过去某一个时间点。oracle闪回查询使用的是undo表空间,闪回查询能恢复的时间点取决于undo表空间的大小和Undo_Retention参数有关。
1、查看undo表空间的大小和保留时间
SQL>SELECT VALUE/60 AS "UNDO_RETENTION_MINUTES" FROM V$PARAMETER WHERE NAME = 'undo_retention';
SQL>select name from v$tablespace; #查看Undo库文件
SQL>SELECT value AS default_undo_tablespace FROM v$parameter WHERE name = 'UNDOTBS1'; #查看默认回滚表空间名
SQL>select s.STATUS,sum(s.BYTES)/1024/1024 from DBA_UNDO_EXTENTS s group by s.STATUS; #查看undo表空间占用大小
SQL>SELECT tablespace_name, ROUND(bytes / (1024 * 1024 * 1024), 2) AS size_gb,file_name FROM dba_data_files; #查看undo文件存放的位置
SQL>SELECT d.tablespace_name,
ROUND((NVL(a.total, 0) - NVL(f.free, 0)) / 1024 / 1024, 2) AS used_undo_space_mb,
ROUND(NVL(f.free, 0) / 1024 / 1024, 2) AS free_undo_space_mb,
ROUND(NVL(a.total, 0) / 1024 / 1024, 2) AS total_undo_space_mb,
ROUND(NVL(a.total, 0) / 1024 / 1024 - NVL(f.free, 0) / 1024 / 1024, 2) AS remaining_undo_space_mb
FROM dba_tablespaces d,
(SELECT tablespace_name, SUM(bytes) total FROM dba_data_files GROUP BY tablespace_name) a,
(SELECT tablespace_name, SUM(bytes) free FROM dba_free_space GROUP BY tablespace_name) f
WHERE d.tablespace_name = a.tablespace_name(+)
AND d.tablespace_name = f.tablespace_name(+)
AND d.contents = 'UNDO'; #查看数据库UNDO表空间占用空间情况
SQL> ALTER SYSTEM SET UNDO_RETENTION = 36000; 将UNDO_RETENTION 被设置为 3600 秒(即 1 小时),最长可设置为24小时。
2、创建测试表空间、用户和角色
[oracle@myoracle orcl11g]$ mkdir -p /u01/app/oracle/oradata/ceshi #创建存放表空间的目录
SQL> create tablespace ceshi datafile '/u01/app/oracle/oradata/ceshi/ceshi.dbf' size 100M autoextend on next 10M MAXSIZE 500M;
SQL>create user test identified by 123456 default tablespace ceshi;
SQL> grant dba to test;
SQL> commit;
SQL> conn test/123456
3、创建测试表
(1)创建一个简单的测试表
SQL>CREATE TABLE test (id NUMBER PRIMARY KEY,data VARCHAR2(100));
(2)插入测试数据
SQL>INSERT INTO test (id, data) VALUES (1, 'Sample data 1');
SQL>INSERT INTO test (id, data) VALUES (2, 'Sample data 2');
SQL>INSERT INTO test (id, data) VALUES (3, 'Sample data 3');
SQL> commit;
(3)查询测试数据
SQL>SELECT * FROM test;
(4)模拟使用delete删除数据
SQL>delete from test where id=2;
四、数据恢复
1、查看所有表的创建和最后修改时间
SQL>SELECT OBJECT_NAME, CREATED, LAST_DDL_TIME from user_objects;
查看特定表的创建和最后修改时间
SQL>select CREATED,LAST_DDL_TIME from user_objects where object_name=upper('test');
2、创建一个临时表,使用AS OF TIMESTAMP进行恢复
方法1:查询1分钟之前的数据
SQL> select * from test as of timestamp (systimestamp - interval '1' minute); #写法1
SQL> select * from test as of timestamp sysdate - 1/1440; #写法2
方法2:指定恢复到某个时间节点
SQL>create table tmp as select * from test AS OF TIMESTAMP to_timestamp('2024-09-28 17:55:00','yyyy-mm-dd hh24:mi:ss'); #此时间段也是根据操作时间进行预估
SQL>SELECT * FROM tmp; # 查询数据
转化为insert语句
SQL> SELECT 'INSERT INTO test (id, data) VALUES(' || '''' || id || '''' || ',' || '''' || data || '''' || ');' FROM tmp ORDER BY id;
**插入恢复的数据**
SQL> INSERT INTO test (id, data) VALUES('2','Sample data 2');
SQL> select * from test;
五、总结
闪回查询能恢复的时间点取决于undo表空间的大小和Undo_Retention参数有关。如果系统中Undo管理比较不合理,事务Undo数据量比较大而且频繁,有失效的Undo前镜像被覆盖之后,恰恰有一个长时间查询需要访问这个前镜像。这个时候,不好恢复了。
事实上,Undo_retention是一个“目标期望值”。用户设置出这个值之后,Oracle内部会尽量保证将Undo数据保留超过undo_retention设置的时间。在这个过程中,Oracle会涉及到比如尝试拓展Undo表空间数据文件、Undo Segment管理等内容。但是,如果“现实比较残酷”,比如说Undo使用紧张、没有额外的方法,那么这个时间段也是不能保证的。
还有一种方式是使用闪回功能,但生产中一般不建议开启闪回功能,闪回也属于杀敌一千自损八百。
欢迎关注公众号《小周的数据库进阶之路》,更多精彩知识和干货尽在其中。