暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

回滚段与事务

DB小榴莲 2018-11-28
2754

一、认识UNDO


ORACLE的逻辑存储结构如下图所示,表空间--->段--->区--->块


 

一般的存放数据文件的表空间,往表空间里建几张表就有几个段,一个对象对应一个段(这里不讨论特殊情况)。


 

UNDO表空间和普通的数据表空间不同,ORACLE自动生成UNDO表空间,自动生成UNDO段,自动分配UNDO区,自动使用UNDO段,只要保证UNDO表空间的大小就可以,UNDO段自动维护。


9i之前,ORACLE自动生成UNDO表空间,自动分配UNDO段,手工分配UNDO区,如果UNDO段用完之后没有及时分配UNDO区,事务会挂起,大的DML操作经常因为UNDO段不够而失败。

 

注:系统中会同时存在两个回滚段:系统表空间回滚段和UNDO表空间回滚段


在SYSTEM表空间里也有一个回滚段,什么情况下才会用到SYSTEM表空间里的UNDO回滚段呢?


  • ORACLE操作数据字典时,比如建表,需要更新ORACLE的数据字典,而ORACLE的数据字典在SYSTEM表空间里,这时会使用SYSTEM表空间里的UNDO回滚段


  • 如果UNDO表空间损坏,也会使用SYSTEM表空间里的UNDO段,ORACLE为了防止UNDO表空间损坏导致数据库启动失败,在系统表空间里预留少量的UNDO回滚段使用并备用

 

查看数据库当前UNDO表空间

SQL> show parameter undo_tablespace;

 

查看回滚段表空间由哪些数据文件组成

SQL> select file_name,bytes/1024/1024/1024 from dba_data_files where tablespace_name like '%UNDOTBS%';

 

查看回滚段表空间里的段信息

SQL> select * from v$rollname;

 

查看某个段

SQL> select segment_name,blocks,extents from dba_segments where segment_name='SYSSMU1$';

 

查看回滚段占用的区

SQL> select segment_name,tablespace_name,extent_id,file_id,blocks from dba_extents where segment_name='_SYSSMU1$';

 

 

二、UNDO的作用


2.1、回滚


ORACLE开始一个事务的时候,开始使用UNDO表空间。


一个事务开始时,ORACLE会拷贝一份修改前的数据放到UNDO表空间的UNDO段里,事务修改的块越多,必然使用的UNDO段就越多,当一个事务未完成就意外终止,ORACLE会自动回滚该事务,就是把修改前的数据再拷贝回来。

 

 

2.2、提供读一致性


A会话开始一个事务,把1改成2,未提交;B会话上来只能看到1,看不到2。


为什么会这样呢?B怎么会看到1而不是2呢?


原因:B通过构造CR块达到一致性读的目的。因为1在被修改成2之前就被拷贝到UNDO段里,B上来发现A会话没有提交,就在buffer cache里额外申请一个数据块,把回滚段里的数据1的镜像拷贝写到buffer cache供B会话读取。

 

2.3、实例恢复


ORACLE用redo log做实例恢复,前滚时里面有未提交的事务,这部分事务需要回滚(rollback)

 

 

三、UNDO段中区的状态


  • free

  • inactive

  • active

  • expired

 

检查undo段的状态

SQL> select usn,xacts,rssize/10241024/1024,hwmsize/1024/1024/1024,shrinks from v$rollstat order by rssize;

 

查看区的状态

SQL> select extent_id,bytes,status from dba_undo_extents where  segment_name='_SYSSMU1$';

 

 

如上图所示, UNDO表空间内分配了5个UNDO回滚段,绿色的段1,3,4,5表示没有被分配,它们的状态是Free;


A事务开始时,选择使用segment2,随着A事务的不断进行并且不提交,segment2会不断分配新的undo区,这些区此时的状态是Active;


A事务一旦提交,事务所占用的区就变成Inactive状态,但是ORACLE不一定释放这部分空间(ORACLE尽量不释放,但是有时空间不够也会覆盖它们),因为ORACLE为了实现某一部分功能,希望已提交事务对应的UNDO表空间中的UNDO段中的UNDO区中的数据尽量保留一段时间,这段时间由参数undo_retention(默认900s ,15min)决定,过了这个时间,UNDO区的状态就变成expired,一般的,expired状态的区不会被释放成free 。


undo_retention这个参数的作用就是保证在规定的时间内,状态为Inactive的区不被覆盖(expired),如果空间压力大,Inactive状态的区也会被覆盖(expired),因为理论上是可以被覆盖的。


如果强烈希望ORACLE在undo_retention规定的时间内Iinactive状态的区不被覆盖,可以将表空间的模式设置成guarantee


SQL> alter tablespace undotbs1 retention guaranteee;

SQL> alter tablespace undotbs1 retention noguarantee;

 

查看更改情况 

SQL> select tablespace_name,retention from dba_tablespace;

 

事务开始会占用UNDO段,不够了就优先找free,free也没有就自动扩展UNDO表空间,扩容后就有free了。空间扩到不能再扩了,就用expired,用完了再扩;expired没有了,就用inactive,如果在guarantee模式,inactive状态的区不被覆盖,事务就挂起。

 


四、图解一个事务的操作流程

 

4.1 事务表、事务槽、回滚块的概念


UNDO表空间的UNDO段的第一个数据块(段头块)里存在一张事务表,这张事务表有47行,意味着每一个UNDO段头块可以有47个事务,一行一个,写上事务id(xid)。一个UNDO段最多可以同时有47个活动事务,意味着47个活动事务共用一个回滚段。


ORACLE尽量让一个事务占一个回滚段,实在没办法才让多个事务占一个回滚段,ORACLE还会尽量均匀将事务分配在各个回滚段上。


 

当一个事务开始时候,ORACLE给每一个事务分配一个事务ID(XID),事务在回滚段表空间里寻找回滚段,首先在undo段头块的事务表里的某一个空行里写上自己的信息,同时,该事务还需要在被修改的数据块头的事务槽(1-255)里,找个空的事务槽写上自己的信息。


就是说事务表在UNDO段头块里,事务槽在被修改的数据块里,事务ID用来标识每一个不同的事务。


4.2 图解一个事务过程


 


1.事务开始,ORACLE会为每一个事务分配一个事务ID(XID),在undo表空间里的undo段的段头块里的事务表里找一行写上该事务ID,同时分配一个undo block 1,并将undo block 1的地址(UBA)写到事务表里,与事务表中的XID对应。XID既是一个事务编号也是一个地址,XID由块号、行号、被覆盖次数组成,三者共同标识唯一一个事务,通过XID可以找到事务表。


2.该事务若是要修改一个数据块,首先在该数据块头部的事务槽里找一行写上XID,XID与回滚段段头块里的XID对应。


3.若是修改目标数据块里的某五行数据,这五行数据就被写到了第一次分配的undo block 1。


4.当然,一个事务可以修改不止一个数据块,如果有第二个数据块被修改,同样的,也要在第二个即将被修改的数据块头部的事务槽里找一行写上XID,与回滚段段头块中的事务表里的XID对应。


5.在修改第二个数据块时,第一次分配的undo块如果已经被写满了,则要重新分配一个undo block 2,并将第二个被修改的数据块中的相应的行写到undo block 2中去。


6.事务表里的UBA指向最新的undo block 2。


 

undo block 1和undo block 2是存在先后顺序的,并且新老undo块串联起来,新的undo块指向上一个老的undo块。因为写undo块的时候是从undo block 1写到undo block 2再写到undo block 3,当需要做rollback的时候,需要先回滚undo block3 ,再回滚undo block 2,最后才是undo block 1。

 


五、模拟一个事务


向一张表里插入信息,模拟一个事务的开始

SQL>select * from t;

SQL>insert into t values(1,'kkk');

 

在事务开始之前和之后分别查询事务ID,只有在事务开始之后才可以查看到事务IDXID

SQL>select xid,xidusn,xidslot,xidsqn,ubablk,ubafil from v$transaction;


查看UNDO段的段头块信息

select * from v$rollname;

 

查看回滚段断头块的地址

select header_block,header_file from dba_segments where segment_name='_SYSSMU1$';


把段头块dump下来,转储某个段头

alter system dump undo header '_SYSSMU1$';

  

也可以转储段头块

alter system dump datafile 5 block 4308;


查看当前会话对应的进程ID,段头块以spid命名

select spid from v$process where addr in (select paddr from v$session) where sid = (select sid from v$mystat where rownum = 1);


 


 

 


最后修改时间:2020-01-04 19:39:28
文章转载自DB小榴莲,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论