undo详解

1.首先了解什么是SCN?

时间换算过来的一个递增数字;保证了数据的一致性!不用时间省去比较的麻烦所以用scn!

当前scn号和时间的对应关系:


sys@WENCHAOD> select dbms_flashback.get_system_change_number,SCN_TO_TIMESTAMP(dbms_flashback.get_system_change_number) from dual;

GET_SYSTEM_CHANGE_NUMBER SCN_TO_TIMESTAMP(DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER)
------------------------ ---------------------------------------------------------------------------
                 1387175 12-MAR-14 11.35.56.000000000 AM

sys@WENCHAOD> 



查询当前scn:


sys@WENCHAOD> select CURRENT_SCN from v$database;

CURRENT_SCN
-----------
    1386439


但实际上,Oracle 在内部并不是用数字来存储SCN的。


在Oracle内部,SCN分为两部分存储,分别称之为scn wrap和scn base。实际上SCN长度为48位,即它其实就是一个48位的整数。只不过可能是由于在早些年通常只能处理32位甚至是16位的数据,所以人为地分成了低32位(scnbase)和高16位(scn wrap)。


为什么不设计成64位,这个或许是觉得48位已经足够长了并且为了节省两个字节的空间:)。那么SCN这个48位长的整数,最大就是2^48(2的48次方, 281万亿,281474976710656),很大的一个数字了。


这里有一个重要的公式:

   SCN= (SCN_WRAP* 4294967296) + SCN_BAS


根据上面的公式,可以计算出SCN的数据值。


在很多与我们事务相关的记录中都是记录SCN WRAP 和 SCN BASE.

sys@WENCHAOD> select START_SCNB,START_SCNW  from v$transaction;

START_SCNB START_SCNW
---------- ----------
         0          0

sys@WENCHAOD> desc smon_scn_time;
 Name                                                  Null?    Type
 ----------------------------------------------------- -------- ------------------------------------
 THREAD                                                         NUMBER
 TIME_MP                                                        NUMBER
 TIME_DP                                                        DATE
 SCN_WRP                                                        NUMBER
 SCN_BAS                                                        NUMBER
 NUM_MAPPINGS                                                   NUMBER
 TIM_SCN_MAP                                                    RAW(1200)
 SCN                                                            NUMBER
 ORIG_THREAD                                                    NUMBER

sys@WENCHAOD> 



在数据库中那些地方用了scn号:



控制文件中针对每一个dbf文件有一个scn号(fscn)和一个结束scn(escn )号,在dbf文件头部有个start scn号  ---->为了保证文件的一致性!

开始时fscn=start scn,end scn为空!





sys@WENCHAOD> select checkpoint_change# from v$database;

CHECKPOINT_CHANGE#
------------------
           1360194
sys@WENCHAOD> select name ,checkpoint_change# from v$datafile;

NAME
----------------------------------------------------------------------------------------------------
CHECKPOINT_CHANGE#
------------------
/u01/app/oracle/oradata/wenchaodb/system01.dbf
           1360194

/u01/app/oracle/oradata/wenchaodb/sysaux01.dbf
           1360194

/u01/app/oracle/oradata/wenchaodb/undotbs01.dbf
           1360194

/u01/app/oracle/oradata/wenchaodb/users01.dbf
           1360194

/u01/app/oracle/oradata/wenchaodb/example01.dbf
           1360194


sys@WENCHAOD> select name,last_change# from v$datafile;

NAME
----------------------------------------------------------------------------------------------------
LAST_CHANGE#
------------
/u01/app/oracle/oradata/wenchaodb/system01.dbf


/u01/app/oracle/oradata/wenchaodb/sysaux01.dbf


/u01/app/oracle/oradata/wenchaodb/undotbs01.dbf


/u01/app/oracle/oradata/wenchaodb/users01.dbf


/u01/app/oracle/oradata/wenchaodb/example01.dbf



sys@WENCHAOD> 

--数据文件头部scn号(其实和上面的scn应该相等,但是我查的时候不一样)

sys@WENCHAOD> select name ,checkpoint_change# from v$datafile_header;

NAME
----------------------------------------------------------------------------------------------------
CHECKPOINT_CHANGE#
------------------
/u01/app/oracle/oradata/wenchaodb/system01.dbf
           1394461

/u01/app/oracle/oradata/wenchaodb/sysaux01.dbf
           1394461

/u01/app/oracle/oradata/wenchaodb/undotbs01.dbf
           1394461

/u01/app/oracle/oradata/wenchaodb/users01.dbf
           1394461

/u01/app/oracle/oradata/wenchaodb/example01.dbf
           1394461


sys@WENCHAOD> 



数据库正常关闭时,buffer cache写到磁盘上,同时更新系统scn,fscn,start scn,用关闭的时间点去更新!同时将escn设为和其他的一样,,即正常关闭后四个scn一样


非正常关闭,escn为空----->需要实例恢复,,,,其他三个一样,,

1.需要redo log部分,不要归档log


如果把其中一个文件删除,换了一个备份文件,Oracle启动时发现scn不一致,使用日志去跑成一样

跑日志:---------->提升scn:----->需要归档日志和redo合起来泡成新的

全部换成是旧的会怎么样??



备份的时候,控制文件,数据文件,日志文件全部备份了------------------->不会跑日志


日志文件scn


每条日志都有scn


每个日志文件都有两个scn  :firsr scn ,next scn;


sys@WENCHAOD> select * from v$log;

    GROUP#    THREAD#  SEQUENCE#      BYTES  BLOCKSIZE    MEMBERS ARC STATUS           FIRST_CHANGE#
---------- ---------- ---------- ---------- ---------- ---------- --- ---------------- -------------
FIRST_TIME   NEXT_CHANGE# NEXT_TIME
------------ ------------ ------------
         1          1         22   52428800        512          1 NO  CURRENT                1394461
12-MAR-14      2.8147E+14

         2          1         20   52428800        512          1 NO  INACTIVE               1340799
11-MAR-14         1360194 12-MAR-14

         3          1         21   52428800        512          1 NO  INACTIVE               1360194
12-MAR-14         1394461 12-MAR-14


sys@WENCHAOD> 

下一个first就是上一个日志的next;;;可以看作first是该日志文件的第一条日志的scn号,next认为是最后一条scn!!!!


数据库正常期间:

current日志的first scn  ====数据文件的scn  ===比如1234567

数据库突然崩了,需要1234567后的日志去恢复,而他的日志在current里面,所有只需要current日志!!!

alter system switch_logfile  ---执行两次(切换时系统及数据文件scn号不改变,他们主要的作用是表示文件的新旧程度和一致程度)

需要三个日志恢复   --------记住:需要几个根据first scn号和(系统scn号或者是数据文件scn号比较)


日志文件的三种状态:

inactive:buffer cache 数据已写回磁盘
active:日志文件的日志所对应的脏缓冲区还没有写回去(磁盘),日志不能被覆盖  
current:log buffer正在使用中


file csn ,数据文件头部scn,系统scn===日志文件current或者是active   scn号

注意:如果等于current的话,其他两个一定是inactive状态


文件 系统的scn====最老的active的scn



跑日志的时候(实例恢复)先定位到日志文件(根据文件、系统、数据文件头部的scn),再根据control文件中记录的LRBA定位到具体的日志条目!

所以检查点进程发生的时候没有更新 文件、系统、数据文件头部的scn


总结一下:

检查点进程发生的时候没有更新 文件、系统、数据文件头部的scn,只有当日志文件从active变为inactive时才会更新,end scn只有当数据库关闭的时候才会更新


以往的first ,next


sys@WENCHAOD> select * from v$log_history where rownum<6;

     RECID      STAMP    THREAD#  SEQUENCE# FIRST_CHANGE# FIRST_TIME   NEXT_CHANGE#
---------- ---------- ---------- ---------- ------------- ------------ ------------
RESETLOGS_CHANGE# RESETLOGS_TI
----------------- ------------
         1  839953789          1          1        945184 19-FEB-14          979310
           945184 19-FEB-14

         2  839953822          1          2        979310 19-FEB-14          985045
           945184 19-FEB-14

         3  839953958          1          3        985045 19-FEB-14          994949
           945184 19-FEB-14

         4  839954066          1          4        994949 19-FEB-14         1011501
           945184 19-FEB-14

         5  839954709          1          5       1011501 19-FEB-14         1020035
           945184 19-FEB-14


sys@WENCHAOD> select * from v$log;

    GROUP#    THREAD#  SEQUENCE#      BYTES  BLOCKSIZE    MEMBERS ARC STATUS           FIRST_CHANGE#
---------- ---------- ---------- ---------- ---------- ---------- --- ---------------- -------------
FIRST_TIME   NEXT_CHANGE# NEXT_TIME
------------ ------------ ------------
         1          1         22   52428800        512          1 NO  CURRENT                1394461
12-MAR-14      2.8147E+14

         2          1         20   52428800        512          1 NO  INACTIVE               1340799
11-MAR-14         1360194 12-MAR-14

         3          1         21   52428800        512          1 NO  INACTIVE               1360194
12-MAR-14         1394461 12-MAR-14


sys@WENCHAOD> select * from v$archived_log;

no rows selected

sys@WENCHAOD> 







2.什么是检查点队列

buffer cache ,里面单独有块区域存放链(chain),把buffer链起来!LRU,,LRUW(脏块的访问频率,冷端,热端),CBC。(Google)。。,,为了组织管理小buffer !还有一种链叫检查点队列链,check point chain(按照块第一次脏的时间)!该链上,脏块!
CKPT两种工作方式:

1:完全检查点:ckpt触发dbwr将所有的脏块写到磁盘上!!!!关闭数据库时触发

2:增量检查点:ckpt将检查点队列链上的第一块的LRBA地址记录到控制文件中!!!!每隔3秒触发!!如果发现脏块太多,IO不是很忙,结合这两个因素触发dbwr,缩短chain!


select cpdrt,cplrba_seq||'.'||cplrba_bno||'.'||cplrba_bof  "low RBA",cpodr_seq||'.'||cpodr_bno||'.'||cpodr_bof "on disk RBA",cpods,cpodt,cphbt from x$kcccp ;


--cpdrt:检查点队列中脏块的数目

--cpods:on disk rba的scn

--cpodt:on disk rba的时间戳

--cphbt:心跳


sys@WENCHAOD> alter system flush buffer_cache; --清除所有脏块



3.实例恢复

服务器突然掉电,buffer cache中的脏块没有写到磁盘上,出现数据丢失:
1:可以丢失的
2:不能丢失的
那些可以丢失:
没提交的事务所修改的数据块可以丢失
不可以丢失:
已提交的事务所修改的数据块不可以丢失
情况一:
事务已经提交但是还没有写到磁盘上去?(丢失不能接受)---->通过日志找回(前滚)
情况二:
事务还没有提交?(可以接受)
如何通过日志找回?
一部分在buffer日志中,一部分写到磁盘上了
在buffer中说明事务还没有提交,如果提交了,他就一定写到磁盘上了(commit触发)!
数据库重启动时,自动发现数据库非正常关闭,做实例崩溃恢复,使用Oracle磁盘上的日志(redo log)将数据库崩溃瞬间构造出脏块,回到出事瞬间!
到底是用那些日志恢复呢?
终点:on disk rba (current日志文件的最后一条日志)
起点:崩溃之前 check point chain上第一块的LRBA地址,记住,如前面解析,该地址已经记录到控制文件中
但是,有可能把未提交的事务构造出来,对所有未提交的事务(Oracle在undo中已经记录了)进行回滚!见后面解析!!!!!!

4.事务,回滚详解

数据块有scn,日志有scn!!!日志多了 --->空跑日志

事务开始:

一组DML语句:insert delete  update第一条
事务结束:
commit
rollback
假设:
执行5条DML语句,修改了8个块,6个块对应的日志写入redo log中,2个在log buffer中------》db崩了------------》重启Oracle跑日志(LRBA到on disk rba)--------->六个块被构造出来----------------》事务依赖的会话已死,会话不可能重新完成----------》事务只能被回滚--------》六个块所做的修改自动被rollback
事务是保证数据一致性的一个手段!!
事物的隔离级别:
默认:set transaction isolation level read  commit;
一个事务提交以前,别的会话看不到未提交的数据的,提交后可以
serializable级别:
都没有

undo表空间

三个作用:
1:读一致性,构造cr块
2:回滚
3:实例恢复
undo表空间自动生成,自动有了一堆undo段,段是自动使用的,不需要认为创建!
从某种意义来讲,只有保证undo表空间的空间大小就可以了,不需要关注undo段如何需要,但是高级dba是要掌握的,解决Oracle性能问题
sys@WENCHAOD> show parameter undo_table

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
undo_tablespace                      string      UNDOTBS1
sys@WENCHAOD> 
undo表空间占用空间情况以及数据文件存放位置
sys@WENCHAOD>  select file_name,bytes/1024/1024 from dba_data_files where tablespace_name like '%UNDO%';

FILE_NAME
----------------------------------------------------------------------------------------------------
BYTES/1024/1024
---------------
/u01/app/oracle/oradata/wenchaodb/undotbs01.dbf
             90


sys@WENCHAOD> 

undo段:
sys@WENCHAOD> select * from v$rollname;


       USN NAME
---------- ------------------------------
         0 SYSTEM
         1 _SYSSMU1_3780397527$
         2 _SYSSMU2_2232571081$
         3 _SYSSMU3_2097677531$
         4 _SYSSMU4_1152005954$
         5 _SYSSMU5_1527469038$
         6 _SYSSMU6_2443381498$
         7 _SYSSMU7_3286610060$
         8 _SYSSMU8_2012382730$
         9 _SYSSMU9_1424341975$
        10 _SYSSMU10_3550978943$


11 rows selected.


sys@WENCHAOD> 

1到10在undo表空间里面,0 在系统表空间里面!!
睡着事务的增加,undo中自动调整
只有在Oracle对数据字典操作的时候才需要使用system回滚段!!!!比如说建表,对数据库的对象进行增加删除的时候才会用到!!
undo坏了也会使用system段!!!!!
查询某个段的使用情况
select  segment_name , blocks,extents from dba_segments where segment_name like '%SYSSMU%';
占用了哪些区?

9i以前undo自动管理,但是区需要手动分配!!!!9i开始全自动,只要undo表空间容量够!!
sys@WENCHAOD> show parameter undo

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
undo_management                      string      AUTO
undo_retention                       integer     900
undo_tablespace                      string      UNDOTBS1
sys@WENCHAOD> 

undo作用,如何使用?undo表空间该设多大??

作用:开始一个事务的时候需要用到!比如delete,Oracle把修改前的数据放到undo表空间的undo段(自动随机)中!-------回滚!!!!修改的数据越多,undo使用的越多!
   读一致性,构造(cr块)!
            实例崩溃恢复中的回滚
根本原因:保存了修改前的数据!!!!!
undo段中区的状态
free          -----------------------没有使用
expired    ---------------------  事务提交后,Oracle希望再保存一段时间(undo_retention时间),可以覆盖
inactive   ---------------------事务提交后,不可以覆盖(undo_retention时间后),但是如果undo空间压力大时,也可以覆盖!但是如果一定想要保存那段时间,修改表空间状态为guarantee
alte tablespace undotbs1 retention guarantee/noguarantee       (desc dba_tablespace)
active      --------------事务使用段,未提交
查询undo段
select usn,xacts,rssize /1024/1024/1024,hwmsize/1024/1024/1024,shrinks from v$rollstat order by rssize;
sys@WENCHAOD> select usn,xacts,rssize/1024/1024/1024,hwmsize/1024/1024/1024,shrinks from v$rollstat order by rssize;

       USN      XACTS RSSIZE/1024/1024/1024 HWMSIZE/1024/1024/1024    SHRINKS
---------- ---------- --------------------- ---------------------- ----------
         0          0            .000358582             .000358582          0
         1          0            .001091003             .002067566          1
         5          0            .001091003             .002067566          2
         9          0            .001091003             .004997253          1
         6          0            .001091003             .002067566          1
        10          0            .001091003             .003044128          1
         8          0            .002067566             .023551941          6
         7          0            .002067566             .003044128          1
         4          0            .002067566             .005973816          2
         3          0            .002067566             .002067566          1
         2          0            .002067566             .002128601          1

11 rows selected.

sys@WENCHAOD> 
显示undo区的状态信息
select extent_id,bytes,status from dba_undo_extents where segment_name like  '%SYSSMU%';
sys@WENCHAOD> select extent_id,bytes,status from dba_undo_extents where segment_name like  '%SYSSMU%';

 EXTENT_ID      BYTES STATUS
---------- ---------- ---------
         0      65536 EXPIRED
         1      65536 EXPIRED
         2    1048576 UNEXPIRED
         0      65536 EXPIRED
         1      65536 EXPIRED
         2    1048576 EXPIRED
         3    1048576 UNEXPIRED
         0      65536 EXPIRED
         1      65536 EXPIRED
         2    1048576 UNEXPIRED
         3    1048576 EXPIRED
         0      65536 EXPIRED
         1      65536 EXPIRED
         2    1048576 UNEXPIRED
         3    1048576 EXPIRED
         0      65536 EXPIRED
         1      65536 EXPIRED
         2    1048576 UNEXPIRED
         0      65536 EXPIRED
         1      65536 EXPIRED
         2    1048576 UNEXPIRED
         0      65536 EXPIRED
         1      65536 EXPIRED
         2    1048576 UNEXPIRED
         3    1048576 EXPIRED
         0      65536 EXPIRED
         1      65536 EXPIRED
         2    1048576 EXPIRED
         3    1048576 UNEXPIRED
         0      65536 EXPIRED
         1      65536 EXPIRED
         2    1048576 UNEXPIRED
         0      65536 EXPIRED
         1      65536 EXPIRED
         2    1048576 UNEXPIRED

35 rows selected.

sys@WENCHAOD> 

图解一个事务的操作流程

事务开始---->oracle分配事务ID(XID)----->在undo表空间中找一个undo段,在undo段的段头块(第一个数据块)里面有一个事务表(每一个回滚段最多47个事务),找其中一行写上事务信息(xid:UBA undo块地址)------------->同时给事务分配undo块---->将undo块的地址写到事务表中---->
---->修改数据块前,首先在事务槽中写上(XID:UBA)----->将修改前数据保存到undo块中
一个undo段最多可以有47个活动事务,Oracle尽量的一个事务使用一个回滚段,尽量分布均匀!!!!
XID即是事务id也是事务地址!(XID:那个回滚段的段头块,事务表哪行,该行第几次被使用)---->唯一标识事务表哪行?假设没有第三个,事务id可能一样

数据块的块头有事务槽(ITL),在事务修改数据块(可能修改了很多块)的时候,在数据块的头部(255)写上事务信息
总结:
事务开始,在两个位置写上事务信息,1,事务表;2,事务槽!为什么?---Oracle事务的提交方式

dump到$Oracle_base/admin/$SID/udump/$spid中

回滚块:存放修改前的数据!
uba指向最新的回滚块,回滚时根据事务表uba找到最新的,
数据块直接指向undo是为了在读一致性是构造CR块时很方便的构造出来

数据块事务槽xid指向事务表:为了Oracle的一种提交方式,稍后解析!!!!


你可能感兴趣的:(回滚,undo)