当用户操作不慎导致误删改数据时,闪回技术可以简单快捷地恢复数据。
该技术主要是通过回滚段存储的 UNDO 记录来完成历史记录的还原。设置ENABLE_FLASHBACK 为 1 后,开启闪回功能。DM 会保留回滚段一段时间,回滚段保留的时间代表着可以闪回的时间长度。由 UNDO_RETENTION 参数指定。

下面开始我们的发现之旅。

1、参数设置

设置闪回参数

sp_set_para_value(1,'ENABLE_FLASHBACK',1);

设置回滚段可以保留的时间长度

sp_set_para_double_value(1,'UNDO_RETENTION',10800);

2、测试准备

2.1创建测试表并加载数据,执行检查点

create table test(id int,num int);
insert into test select level,level from dual connect by level <=100000;
commit;
checkpoint(100);

2.2打开几个会话分别收集如下统计信息

####会话1 收集BUFFERPOOL使用情况
select  sum(n_pages),sum(n_phy_write) ,sum(n_dirty)
from v$bufferpool
where name='NORMAL';

行号       SUM(N_PAGES)         SUM(N_PHY_WRITE)     SUM(N_DIRTY)
---------- -------------------- -------------------- --------------------
1          508997               1719                 0

####会话2 收集回滚段信息
SQL>  select * from V$PURGE;

行号       OBJ_NUM     IS_RUNNING PURG_FOR_TS
---------- ----------- ---------- -----------
1          1           Y          N

SQL>  select TRX_ID,TAB_ID,ROW_COUNT from V$PURGE_PSEG_TAB;

行号       TRX_ID               TAB_ID      ROW_COUNT
---------- -------------------- ----------- --------------------
1          91766                1075        100000

SQL>  select nth,n_pages,n_extend,n_used_pages,reserve_time
2    from V$PSEG_ITEMS order by 2 desc limit 1;

行号       NTH         N_PAGES     N_EXTEND    N_USED_PAGES RESERVE_TIME
---------- ----------- ----------- ----------- ------------ ------------
1          0           256         0           0            10800

####会话3 回滚表空间信息
SQL> select
2   (select bytes/1024/1024.0 from dba_data_files where tablespace_name='ROLL')
3   -
4   (select bytes/1024/1024.0 from dba_free_space where tablespace_name='ROLL')
5   UNDO_TABLESPACE_USED_M
6   /

行号       UNDO_TABLESPACE_USED_M
---------- ----------------------
1          10.734375

####会话4 闪回版本查询
SQL>  SELECT VERSIONS_STARTTRXID,VERSIONS_ENDTRXID, VERSIONS_OPERATION ,id,num
2    FROM test VERSIONS BETWEEN TIMESTAMP sysdate-1.0/24 AND SYSDATE
3    where id=1
4   /

行号       VERSIONS_STARTTRXID  VERSIONS_ENDTRXID    VERSIONS_OPERATION ID          NUM
---------- -------------------- -------------------- ------------------ ----------- -----------
1          91766                NULL                 I                  1           1

3、数据更新

3.1更新数据,执行检查点

update test set num=num+1;
commit;
checkpoint(100);

3.2执行查询统计

2.2中的4个会话SQL。

3.3重复执行

执行3.1和3.2

4、查询闪回数据

4.1显示闪回多版本:




达梦索引创建 达梦新增字段_sql


行号       VERSIONS_STARTTRXID  VERSIONS_ENDTRXID    VERSIONS_OPERATION ID          NUM
---------- -------------------- -------------------- ------------------ ----------- -----------
1          91772                NULL                 U                  1           3
2          91767                91772                U                  1           2
3          91766                91767                I                  1           1

已用时间: 407.825(毫秒). 执行号:902.

可以看出存在三个数据版本。91766事务完成INSERT操作,NUM字段为1。91767事务完成UPDATE操作,NUM字段被更新为2。91772事务完成UPDATE操作,NUM字段被更新为3。

4.2根据闪回事务号查询SQL历史执行情况:


达梦索引创建 达梦新增字段_数据库_02


SET LINESHOW OFF
select trx_id,sql_id,seq_no,substr(top_sql_text,1,30) sql,start_time,affected_rows 
from v$sql_history where trx_id in (91766,91767,91772)order by seq_no;

91766                16          20          insert into test select level, 2023-01-15 16:23:14.000000 100000
91767                17          21          checkpoint(100)                2023-01-15 16:23:18.000000 0
91767                25          30          update test set num=num+1;     2023-01-15 16:32:51.000000 100000

TRX_ID               SQL_ID      SEQ_NO      SQL                        START_TIME                 AFFECTED_ROWS
-------------------- ----------- ----------- -------------------------- -------------------------- -------------
91767                26          31          commit;                    2023-01-15 16:32:51.000000 0
91772                27          32          checkpoint(100);           2023-01-15 16:32:51.000000 0
91772                25          39          update test set num=num+1; 2023-01-15 16:37:03.000000 100000
91772                26          40          commit;                    2023-01-15 16:37:03.000000 0

查询出的结果与执行顺序一致

4.3根据SQL历史执行情况中的时间查询闪回数据


达梦索引创建 达梦新增字段_Powered by 金山文档_03


SQL>  select * from(select * from test WHEN TIMESTAMP '2023-01-15 16:32:51' ) where id=1;

ID          NUM
----------- -----------
1           1

已用时间: 141.382(毫秒). 执行号:916.
SQL>  select * from(select * from test WHEN TIMESTAMP ' 2023-01-15 16:37:03' ) where id=1;

ID          NUM
----------- -----------
1           2

已用时间: 90.876(毫秒). 执行号:917.
SQL> select * from test where id=1;

ID          NUM
----------- -----------
1           3

已用时间: 9.606(毫秒). 执行号:918.

查询出的结果与变更顺序一致

注:sql执行时间也可以通过V$PSEG_COMMIT_TRX 视图查询。

如果知道修改时间,可以跳过4.1和4.2直接根据时间查询闪回数据。

5、后台视图分析

5.1回滚段查看


达梦索引创建 达梦新增字段_Powered by 金山文档_04


SQL>  select * from V$PURGE;

行号       OBJ_NUM     IS_RUNNING PURG_FOR_TS
---------- ----------- ---------- -----------
1          3           Y          N

已用时间: 0.437(毫秒). 执行号:706.
SQL>  select TRX_ID,TAB_ID,ROW_COUNT from V$PURGE_PSEG_TAB;

行号       TRX_ID               TAB_ID      ROW_COUNT
---------- -------------------- ----------- --------------------
1          91772                1075        0
2          91767                1075        0
3          91766                1075        100000

已用时间: 0.354(毫秒). 执行号:707.
SQL>  select nth,n_pages,n_extend,n_used_pages,reserve_time
2    from V$PSEG_ITEMS order by 2 desc limit 1;

行号       NTH         N_PAGES     N_EXTEND    N_USED_PAGES RESERVE_TIME
---------- ----------- ----------- ----------- ------------ ------------
1          1           1440        74          1430         10800

已用时间: 0.336(毫秒). 执行号:708.

查询V$PURGE视图显示共有3个待 PURGE 事务。等到10800秒(UNDO_RETENTION)后查询该视图结果为空。

查询V$PURGE_PSEG_TAB 视图显示有三个会话待 PURGE ,分别为91766、91767和91772,均对应着id等于1075的对象,即test表。

查询V$PSEG_ITEMS视图显示回滚段保留时间为10800(RESERVE_TIME)秒,两个回滚段共占据1430个PAGE,每个715PAGE。

注:INSERT操作没有前数据,不占用回滚空间。

5.2查看BUFFER池情况


达梦索引创建 达梦新增字段_sql_05


select  sum(n_pages),sum(n_phy_write) ,sum(n_dirty)
from v$bufferpool
where name='NORMAL';

行号       SUM(N_PAGES)         SUM(N_PHY_WRITE)     SUM(N_DIRTY)
---------- -------------------- -------------------- --------------------
1          508997               1719                 0

已用时间: 0.230(毫秒). 执行号:601.
SQL> /

行号       SUM(N_PAGES)         SUM(N_PHY_WRITE)     SUM(N_DIRTY)
---------- -------------------- -------------------- --------------------
1          508997               2842                 0

已用时间: 0.363(毫秒). 执行号:602.
SQL> /

行号       SUM(N_PAGES)         SUM(N_PHY_WRITE)     SUM(N_DIRTY)
---------- -------------------- -------------------- --------------------
1          508997               3967                 0

已用时间: 0.423(毫秒). 执行号:603.

N_PHY_WRITE为实例开机以来系统缓冲区累计物理写的次(页)数。由上可见,每次均比前值多大约1124个页面。

查询test表大小


达梦索引创建 达梦新增字段_Powered by 金山文档_06


SQL> select bytes/page from dba_segments where segment_name='TEST';

BYTES/PAGE
--------------------
432

已用时间: 120.798(毫秒). 执行号:903.

从上面得知本次测试回滚段715个页面。432+715=1147,去除控制块等内部结构,基本等于上面的1124。

注:回滚段保存修改前的数据。本测试是全表更新,所以理应大小与表相等。但实际测试大很多。分析原因是回滚段中除了保存完整的修改前数据以外,还需要保存数据的逻辑地址和版本号、事务号以及修改时间,以支持闪回查询。

每次commit后执行检查点(checkpoint(100))会将BUFFER池中表数据和回滚段数据一起写入磁盘。表示数据写入用户表空间。回滚段数据写入ROLL表空间。

5.3查看回滚表空间使用情况


达梦索引创建 达梦新增字段_达梦索引创建_07


SQL> select
2   (select bytes/1024/1024.0 from dba_data_files where tablespace_name='ROLL')
3   -
4   (select bytes/1024/1024.0 from dba_free_space where tablespace_name='ROLL')
5   UNDO_TABLESPACE_USED_M
6   /

行号       UNDO_TABLESPACE_USED_M
---------- ----------------------
1          10.734375

已用时间: 5.434(毫秒). 执行号:801.
SQL> /

行号       UNDO_TABLESPACE_USED_M
---------- ----------------------
1          14.359375

已用时间: 6.899(毫秒). 执行号:802.
SQL> /

行号       UNDO_TABLESPACE_USED_M
---------- ----------------------
1          19.984375

已用时间: 6.395(毫秒). 执行号:803.

由上可见,ROLL表空间一直在增长。

6、结论

可以通过“select * from 表名 WHEN TIMESTAMP ' 时间'”查询过去某一个时点的数据。如果时间点不明确,可以查询V$PURGE_PSEG_TAB视图查看曾经修改某表的事务号,再通V$PSEG_COMMIT_TRX视图或者V$SQL_HISTORY视图查询事务提交时间。

设置ENABLE_FLASHBACK 为 1 后,才能使用闪回查询。ENABLE_FLASHBACK 等于 0时系统照样会保存回滚段信息, V$PURGE、V$PURGE_PSEG_TAB、V$PSEG_COMMIT_TRX、V$PSEG_ITEMS 等视图有效。以上视图数据保留由UNDO_RETENTION 参数决定。

select  name,sum(n_pages),sum(n_phy_write) ,sum(n_dirty)
from v$bufferpool
 where name='NORMAL' or name='ROLL'
group by name

行号       NAME      SUM(N_PAGES)         SUM(N_PHY_WRITE)     SUM(N_DIRTY)
---------- ------    ------------ -------------------- --------------------
1          NORMAL     508997               3967                 0
2          ROLL       128                  0                    0

回滚段数据的物理存储空间是ROLL表空间,但对应的内存空间是BUFFER缓冲区,不是ROLL缓冲区。这一点没有找到官方文档中相应解释。