OCA-UNIT9-UNDO(深入解析)

来源:互联网 发布:现在淘宝代销好做吗 编辑:程序博客网 时间:2024/06/03 12:36
一: SESSION与TRANSACTION的区别
SQL> conn hr/hr
Connected.
SQL> select distinct sid from v$mystat;
SID
----------
159
select sid,username,taddr from v$session s where s.username='HR';
SID USERNAME TADDR
159 HR (空的)
select * from v$transaction;
SQL> drop table test;
Table dropped.
SQL> create table test(a number);
Table created.
SQL> insert into test values(1);
1 row created.
select * from v$transaction;
ADDR XIDUSN XIDSLOT XIDSQN
39A2AC60 6 47 439
select s.sid,s.username
from v$session s,v$transaction t
where s.taddr=t.addr;
SID USERNAME
159
HR
在INSERT那边commit掉
再查
select * from v$transaction;
没了
select * from v$session s where s.username='HR';
select sid,username,taddr from v$session s where s.username='HR';
SID USERNAME TADDR
159 HR (请注意TADDR是空的)
再INSERT
SQL> insert into test values(2);
1 row created.
在另一边用SYS查
select sid,username,taddr from v$session s where s.username='HR';
SID USERNAME TADDR
159 HR 39A2AC60 (TADDR又冒出来了,与之前的一样)
select * from v$transaction;
ADDR XIDUSN XIDSLOT XIDSQN
39A2AC60 9 23 508
再INSERT那ROLLBACK
SQL> rollback;
Rollback complete.
select * from v$transaction;(没了)
select sid,username,taddr from v$session s where s.username='HR';
SID USERNAME TADDR
159 HR (空了)
总结: ROLLBACK可以回退事务,COMMIT可以提交事务。ROLLBACK,COMMIT后均会使事务(TRANSACTION)结束,而会话(SESSION)只要不退出(EXIT或QUIT)会一直在。
继续深入探究
再以HR重新登入
SQL> conn hr/hr
Connected.
select sid,username,taddr from v$session s where s.username='HR';
SID USERNAME TADDR
159 HR (空的并且SID还是159)
PS:这个实验就不验证之前已经验证过的东西了
SQL> insert into test values(3);
1 row created.
select saddr,sid,username,taddr from v$session s where s.username='HR';
SADDR SID USERNAME TADDR
3AF0B4A8 159 HR 39A2AC60 (TADDR仍然是一样的值)
请注意SADDR 他指的是SESSION的ADDRESS(内存地址)
select addr,xidusn,xidslot,ses_addr from v$transaction;
ADDR XIDUSN XIDSLOT SES_ADDR
39A2AC60 5 31 3AF0B4A8(跟上面查出的SESSION ADDR(SADDR)一样)
记住了?
在INSERT那边
QUIT(退出)
再开另一个TERMINAL
[root@ocpdb ~]# su - oracle
[oracle@ocpdb ~]$ sqlplus /nolog
SQL> conn hr/hr
Connected.
select saddr,sid,username,taddr from v$session s where s.username='HR';
SADDR SID USERNAME TADDR
3AEFAED0 145 HR (空的)
select saddr,sid,username,taddr from v$session s where s.username='HR';
SADDR SID USERNAME TADDR
3AEFAED0 145 HR 39A37168(有了)并且跟之前的TADDR不一样了,因为SID换了SADDR也换了,分配给他的内存区域肯定是不一样的)
select addr,xidusn,xidslot,ses_addr from v$transaction;
ADDR XIDUSN XIDSLOT SES_ADDR
39A37168 7 20 3AEFAED0
在INSERT那边
SQL> quit
Disconnected from Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options
[oracle@ocpdb ~]$ sqlplus /nolog
SQL> conn hr/hr
Connected.
select saddr,sid,username,taddr from v$session s where s.username='HR';
SADDR SID USERNAME TADDR
3AEFAED0 145 HR (空的)
(SID一样,SADDR就一样)
在CONN HR/HR那边
SQL> insert into test values(5);
1 row created.
select saddr,sid,username,taddr from v$session s where s.username='HR';
SADDR SID USERNAME TADDR
3AEFAED0 145 HR 39A37168
(看到没,TADDR也就是TRANSACTION的内存地址也一样)
总结: 一个会话连进来,会给你这个会话分配一片内存,我猜,它可能是有一个指针,这个指针指向了另一片内存区域,在你要进行TRANSACTION,也就是事务处理的时候,假设能够在内存中分配这样一片事务区域(我这么说的意思就是它可能分配不了),它就会在指针指向的事务内存区分配(是真正的分配)一片区域给这个事务使用。应该可以这么说(应该),在同一个开机时间内,同一个SID号对应的SADDR应该是一样的,同样的SADDR对应的TADDR应该也会是一样的。(不同的开机时间内,不好说,可能是不一样的)以上这段话,看不懂就跳过吧,看下面的实验。
二: TRANSACTION与UNDO的关系
其实,这个实验与上面的实验是有关联的,之所以分开做因为这样能理解的更加清楚。
与UNDO相关联的几个视图
select tablespace_name,contents,retention from dba_tablespaces d where d.tablespace_name='UNDOTBS1';
TABLESPACE_NAME CONTENTS RETENTION
UNDOTBS1 UNDO NOGUARANTEE
select * from dict d where d.table_name like '%ROLL%';
TABLE_NAME COMMENTS
DBA_ROLLBACK_SEGS Description of rollback segments
V$ROLLNAME Synonym for V_$ROLLNAME
V$ROLLSTAT Synonym for V_$ROLLSTAT
GV$ROLLSTAT Synonym for GV_$ROLLSTAT
select * from dba_rollback_segs;
SEGMENT_NAME OWNER TABLESPACE_NAME SEGMENT_ID
SYSTEM SYS SYSTEM 0
_SYSSMU1$ PUBLIC UNDOTBS1 1
_SYSSMU2$ PUBLIC UNDOTBS1 2
_SYSSMU3$ PUBLIC UNDOTBS1 3
_SYSSMU4$ PUBLIC UNDOTBS1 4
_SYSSMU5$ PUBLIC UNDOTBS1 5
_SYSSMU6$ PUBLIC UNDOTBS1 6
_SYSSMU7$ PUBLIC UNDOTBS1 7
_SYSSMU8$ PUBLIC UNDOTBS1 8
_SYSSMU9$ PUBLIC UNDOTBS1 9
_SYSSMU10$ PUBLIC UNDOTBS1 10
SQL> conn hr/hr
Connected.
SQL> insert into test values(5);
1 row created.
select s.sid,s.username,t.xidusn,xidslot, t.addr
from v$session s,v$transaction t
where s.taddr=t.addr;
SID USERNAME XIDUSN XIDSLOT ADDR
138 HR 10 9 39A136E0
(XIDUSN是回滚段号,XIDSLOT是槽号,也就是说旧值记录在了第10号回滚段的第9个槽位上)
在INSERT那边
SQL> commit;
Commit complete.
SQL> insert into test values(6);
1 row created.
select s.sid,s.username,t.xidusn,xidslot, t.addr
from v$session s,v$transaction t
where s.taddr=t.addr;
SID USERNAME XIDUSN XIDSLOT ADDR
138 HR 2 31 39A136E0
(一个TRANSACTION是以COMMIT或ROLLBACK的结束而结束的,下次INSERT/DELETE/UPDATE/MERGE后它会申请回滚段的另一片区域(另一个段的另一个槽位)),上面它申请的就是段号为2,槽号为31的回滚段空间(也就是防旧值的地方))。
在INSERT的那边
SQL> insert into test values(7);
1 row created.
select s.sid,s.username,t.xidusn,xidslot, t.addr
from v$session s,v$transaction t
where s.taddr=t.addr;
SID USERNAME XIDUSN XIDSLOT ADDR
138 HR 2 31 39A136E0
查出来是一样的值,什么意思?也就是说同一个事务会使用同一个回滚段,一直用一直用它会一直在同一个回滚段上向后扩展,一个区两个区三个区。。。,好,这个时候你如果COMMIT或ROLLBACK,这片区域还在吗?回答你,还在!它会变“灰色”,并不会立刻消亡,什么时候消亡受一个参数限制:
SQL> show parameter undo_retention
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_retention integer 900
这个参数是一个弱势参数,什么意思?它默认是保留900S,也就是15分钟,也就是说,我至少能查到15分钟前的数据,但有个前提,你的UNDO空间没有空间压力,也就是你空间够大,至少要确保15分钟内的旧值都在,注意,是旧值都在!你当然可以把这个参数改到一万年!但是,你的空间要够大,要能存一万年的旧值!当你空间只能存1天的旧值的时候,那么你这个所谓的1万年是假的,它实际上只能存1天!当然,如果你空间能存100万年的旧值,那么哪怕你把这个参数设为1万年,那么它还是能够查到100万年前的数据!明白?那么我们生产环境就会有这样的问题,假设你的UNDO只能保存15分钟的旧值,张三在9:00将TEST表中的7改为8:
Update test set a=8 where a=7;(7在旧值区,8在新值区,不提交行上加了独占锁,接下来示个意)
select s.sid,s.username,t.xidusn,xidslot, t.addr
from v$session s,v$transaction t
where s.taddr=t.addr;
SID USERNAME XIDUSN XIDSLOT ADDR
145 HR 10 36 39A37168
select s.saddr,s.sid,s.username,l.type,l.lmode from v$session s,v$lock l
where s.sid=l.sid and s.username='HR';
SADDR SID USERNAME TYPE LMODE
3AEFAED0 145 HR TM 3
3AEFAED0 145 HR TX 6
TM是表级共享锁
TX是事务锁,或称为行级独占锁
好了,接下来,李四在9:01分发了个SQL语句,这个是个长查询语句(也就是SELECT时间会很长,数据量较大),并且整个语句需要持续14:01秒,也就是说这个语句会在9:15:01秒查到张三更改的这张TEST表(中的这一行),发现李四行上有锁,于是根据这张表所占的块上的事务槽(请注意,是块上的事务槽,不是回滚段上的事务槽)中记载的回滚段号,以及回滚段事务槽号,去找旧值,结果发现,这个区域已经变成了白色(请允许我这么形容,也许是被其他事务占了,反正不见了就是了),也就是说旧值已经倒计时完,消亡了,查不到了,那么ORACLE会报错,一个非常著名的错误,
ORA-01555 SNAPSHOT TOO OLD (快照太旧)
接下来,好戏上演,我们来模拟ORA-01555错误。
三: 模拟ORA-01555 SNAPSHOT TOO OLD (快照太旧)
想想看,我们要想模拟快照太旧,需要什么样的条件?首先,我们想到了UNDO表空间,在生产环境中,建议UNDO表空间不自动扩展,为什么?因为如果生产环境有大量的长查询(长查询的意思就是发一个SELECT语句,到执行结束所消耗的时间较长),那么这个长查询就会一直“要”你的UNDO空间,最后甚至可能会导致你的操作系统空间爆满。UNDO表空间有一个属性-GUARANTEE,也就是说你是否确保长查询优先。在UNDO表空间不扩展的前提下,你不GUARANTEE,那么UNDO_RETENTION这个参数所对应的时间(前面已经讲过这个参数的含义了)将被IGNORE(忽略)。如果你GUARANTEE,那么UNDO_RETENTION这个参数就会听你的,我觉得它会尽量保证你这个参数所对应的时间所需要的UNDO空间(请注意,是在UNDO表空间不自动扩展的前提下,如果UNDO能自动扩展,我们就没有讨论的必要了,它会一直扩到你需要的量为止,除非你的操作系统的空间不足)。回到正题,我们要模拟这个错误,那么就是要让长查询报错,我们有两种选择:
在UNDO表空间不扩展的前提下(但是要足够小!)
(1)
UNDO表空间不GUARANTEE
(2)
UNDO表空间GUARANTEE一个较小的值
其次,我们想到了,我们需要足够大的临时表空间,因为长查询一般都伴随着海量的排序,不能因为排序空间(临时表空间)不足,而导致长查询报错,就不是我们要的ORA-01555了。
至此,两小一大,理论上可以(两小指的是UNDO表空间小,UNDO_RETENTION小,TEMP表空间大)。但是由于,我们做实验的平台是LINUX,LINUX是高度异步的,错误并不是能立刻反映出来。实质上,我们需要三小一大,加入的一个新的小就是:从内存IO到硬盘的时间要小。这里又涉及到两个概念(ORACLE复杂就在于此,一环紧扣一环):
1.
FAST_START_MTTR_TARGET 这个参数的意思就是控制你实例
恢复的时间,你有没有想过这样一个问题,ORACLE每次掉电后重新启动所需要的时间是由什么决定的,为什么有的库掉电后恢复时间短,有的库掉电后恢复的时间长?就是由这个参数决定的!为什么有的库恢复时间长就是因为在你开机的时间段内,内存中的数据没有及时的往下写,下次再开机自然需要多恢复一会了。如果FAST_START_MTTR_TARGET=0,那么内存中的数据往下写是线性的,我的记忆告诉我它15分钟往下写一次(但是请注意,我并没有说内存中的数据是否往下写只由这个参数决定,决定内存中的数据是否往下写还有很多的条件,这里不一一举例了)。所以我们要将FAST_START_MTTR_TARGET的值设小(但是不能为0!),我们可以将它设为1(这个时候它就是非线性的了,它内部计算,算出何时该将内存的数据写入硬盘)(单位是秒)。
2.
将日志组中的每位成员的大小设小,为什么?因为日志的切换会
引起检查点(检查点就是将内存中的脏块往下写到硬盘,脏块就是数据块从硬盘IO到内存后,值被改变的块(可能是块中的一行也可能
是块中的很多行)),日志越小,检查点就越快产生,将内存中的值往下写到硬盘,而且要快就能尽快的将错误报出来。
接下来,准备一下实验环境。我们做这个实验要用到三个脚本,脚本在附件中。
1.
hr_tundo.sql (截取脚本的一部分出来)
create table hr.hr_tundo(uname varchar2(60),uvalue number(9));
insert into hr.hr_tundo values('a1',1);
insert into hr.hr_tundo values('a2',2);
insert into hr.hr_tundo values('a3',3);
insert into hr.hr_tundo values('a4',4);
insert into hr.hr_tundo values('a5',5);
insert into hr.hr_tundo values('a6',6);
insert into hr.hr_tundo values('a7',7);
insert into hr.hr_tundo values('a8',8);
insert into hr.hr_tundo values('a9',9);
……
建一个表,HR_TUNDO,插入10万行记录。
2.
hr_uundo.sql
update hr.hr_tundo set uvalue=3992 where uname='a3991';
update hr.hr_tundo set uvalue=3993 where uname='a3992';
update hr.hr_tundo set uvalue=3994 where uname='a3993';
update hr.hr_tundo set uvalue=3995 where uname='a3994';
update hr.hr_tundo set uvalue=3996 where uname='a3995';
update hr.hr_tundo set uvalue=3997 where uname='a3996';
update hr.hr_tundo set uvalue=3998 where uname='a3997';
update hr.hr_tundo set uvalue=3999 where uname='a3998';
update hr.hr_tundo set uvalue=4000 where uname='a3999';
update hr.hr_tundo set uvalue=4001 where uname='a4000';
update hr.hr_tundo set uvalue=4002 where uname='a4001';
commit;
update hr.hr_tundo set uvalue=4003 where uname='a4002';
update hr.hr_tundo set uvalue=4004 where uname='a4003';
update hr.hr_tundo set uvalue=4005 where uname='a4004';
update hr.hr_tundo set uvalue=4006 where uname='a4005';
update hr.hr_tundo set uvalue=4007 where uname='a4006';
……
更新表中的每一行(为了产生旧值),并且1000行提交一次。(COMMIT后,UNDO的事务槽就会变灰色,旧值开始倒计时)
3.
hr_sundo.sql
select a.uname,a.uvalue, b.uname ,b.uvalue from hr_tundo a, hr_tundo b order by a.uname;
(笛卡尔积+ORDER BY,让它产生海量排序的操作)
开始模拟:
1: 最开始的时候,先执行hr_tundo.sql,先不急着把UNDO表空间缩小,因为等你缩小UNDO后,再建表,会发现UNDO空间不足,无法INSERT,明白?因为对于INSERT来说,也是有旧值的,旧值就是“空行“本身。
SQL> show user
USER is "HR"
SQL> set feedback off
SQL> @/home/oracle/hr_tundo.sql
SQL> select count(*) from hr_tundo;
COUNT(*)
----------
100000
2.
创建一个非常小的UNDO表空间
SQL> show user
USER is "SYS"
SQL> create undo tablespace undotbs3 datafile size 256k;
在操作系统上看
[oracle@ocpdb datafile]$ du -sh o1_mf_undotbs3_6l2z10y3_.dbf
272K o1_mf_undotbs3_6l2z10y3_.dbf(272K可能是因为算法不太一样)
这里需要注意的是,UNDO表空间不能小于128K,因为一个段头就需要128K的空间,如果你等于128K,那么就没有UNDO段了,UNDO表空间也许还能更小,你可以试一下比256K更小的值,看看能不能创建成功(个人感觉还是256K合适,因为我试过140K,查不到一个段)。创建完了就OK了吗?
select tablespace_name,retention from dba_tablespaces t where t.contents='UNDO';
TABLESPACE_NAME RETENTION
UNDOTBS1 NOGUARANTEE
UNDOTBS3 NOGUARANTEE
SQL> show parameter undo_tablespace
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_tablespace string UNDOTBS1
默认用的是UNDOTBS1,一个数据库只能有一个UNDO表空间正在使用,现在要将其切换到UNDOTBS3,注意,切换的时候要“热“的切,也就是数据库要处于OPEN状态。
SQL> alter system set undo_tablespace='UNDOTBS3';
SQL> show parameter undo_tablespace
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_tablespace string UNDOTBS3
select segment_name,status from dba_rollback_segs;
SEGMENT_NAME STATUS
SYSTEM ONLINE
_SYSSMU1$ OFFLINE
_SYSSMU2$ OFFLINE
_SYSSMU3$ OFFLINE
_SYSSMU4$ OFFLINE
_SYSSMU5$ OFFLINE
_SYSSMU6$ OFFLINE
_SYSSMU7$ OFFLINE
_SYSSMU8$ OFFLINE
_SYSSMU9$ OFFLINE
_SYSSMU10$ OFFLINE
(OFFLINE了,SYSTEM是系统使用的回滚段,你用不了,没有UNDOTBS的话,就会用到SYSTEM上,这是非常不好的,要尽量避免)
切换到UNDOTBS3后,就可以删掉UNDOTBS1了。(删不掉的话,需要等待一段时间,因为可能还有些事务(TRANSACTION)在上面。
SQL> drop tablespace undotbs1;
select segment_name,status from dba_rollback_segs;
SEGMENT_NAME STATUS
SYSTEM ONLINE
_SYSSMU1$ ONLINE(这是UNDOTBS3的段)
SQL> alter tablespace undotbs3 retention guarantee;
select tablespace_name,retention from dba_tablespaces t where t.contents='UNDO';
TABLESPACE_NAME RETENTION
UNDOTBS3 GUARANTEE(意思之前说过了,不懂的看前面)
SQL> show parameter undo_retention
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_retention integer 900
SQL> alter system set undo_retention=1;
SQL> show parameter undo_retention
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_retention integer 1
(这里就是我所说的,UNDO表空间不自动扩展,并且GUARANTEE一个较小的值)
select TABLESPACE_NAME,AUTOEXTENSIBLE from dba_data_files
d where d.tablespace_name='UNDOTBS3';
TABLESPACE_NAME AUTOEXTEN
UNDOTBS3 NO(有时候字段会被截断,应该是AUTOEXTENSIBLE,注意)
3.
加大临时表空间所对应的数据文件的大小
select tablespace_name,sum(bytes)/1024/1024 "M" from dba_temp_files t group by t.tablespace_name;
TABLESPACE_NAME M
TEMP 20
[oracle@ocpdb datafile]$ du -sh o1_mf_temp_6k5t4ox6_.tmp
2.0M o1_mf_temp_6k5t4ox6_.tmp(20M目前只用了2M,在SQL语句使用的时候,你可以DU –SH跟踪它的大小,那个时候它才会不断变大)
SQL> show user
USER is "SYS"
SQL> alter database tempfile '/home/oracle/app/oracle/oradata/ORCL/datafile/o1_mf_temp_6k5t4ox6_.tmp' resize 16G;
select tablespace_name,sum(bytes)/1024/1024/1024 "G" from dba_temp_files t group by t.tablespace_name;
TABLESPACE_NAME G
TEMP 16
[oracle@ocpdb datafile]$ du -sh o1_mf_temp_6k5t4ox6_.tmp
2.0M o1_mf_temp_6k5t4ox6_.tmp(前面说过了,到时会自增,最多增到之前RESIZE的16G)
4. 减小日志组,并减小每个日志组下的每个成员的大小
首先,明白一个道理。日志组成员,最少要有两个。为什么?没有两个它怎么切换日志,自己跟自己切吗?是吧。还有一点,日志组每个成员的大小最小为4M,不信你可以试一下。
select group#,members,status from v$log;
GROUP# MEMBERS STATUS
1 2 INACTIVE
2 2 INACTIVE
3 2 CURRENT
10G默认3个组,每个组有两个成员。当前日志组号是3号。
select group#,member from v$logfile;
GROUP# MEMBER
3 /home/oracle/app/oracle/oradata/ORCL/onlinelog/o1_mf_3_6k5t477n_.log
3 /home/oracle/app/oracle/flash_recovery_area/ORCL/onlinelog/o1_mf_3_6k5t49tq_.log
2 /home/oracle/app/oracle/oradata/ORCL/onlinelog/o1_mf_2_6k5t41ym_.log
2 /home/oracle/app/oracle/flash_recovery_area/ORCL/onlinelog/o1_mf_2_6k5t44hy_.log
1 /home/oracle/app/oracle/oradata/ORCL/onlinelog/o1_mf_1_6k5t3x7r_.log
1 /home/oracle/app/oracle/flash_recovery_area/ORCL/onlinelog/o1_mf_1_6k5t3zpm_.log
我建库的时候,采用的是OMF(ORACLE MANAED FILES),
所以它会在闪回区以及ORADATA区各放一个冗余的日志组成员。
为了简便,待会我只会在一个日志组上建一个成员(只是为了简便)。
SQL> alter database add logfile group 4 'home/oracle/app/oracle/oradata/ORCL/onlinelog/redo04.log' size 1M;
alter database add logfile group 4 'home/oracle/app/oracle/oradata/ORCL/onlinelog/redo04.log' size 1M
*
ERROR at line 1:
ORA-00336: log file size 2048 blocks is less than minimum 8192 blocks
(你可以算一下,1M=1024K,log file size 2048 blocks is less than minimum 8192 blocks它认为你有2048BLOCKS,也就是1024K/2048BLOCKS算出每个块是0.5K,它说你不能小于8192BLOCKS,0.5*8192=4M!)
SQL> alter database add logfile group 4 '/home/oracle/app/oracle/oradata/ORCL/onlinelog/redo04.log' size 4M;
SQL> alter database add logfile group 5 '/home/oracle/app/oracle/oradata/ORCL/onlinelog/redo05.log' size 4M;
加完日志组及其成员后,删掉之前的日志组成员。
select group#,members,status from v$log;
GROUP# MEMBERS STATUS
1 2 INACTIVE
2 2 INACTIVE
3 2 CURRENT
(看到没,当前的日志组成员是3,当前的日志是无法删除的,但是我们可以先删除组1与组2。)
SQL> alter database drop logfile group 1;
SQL> alter database drop logfile group 2;
SQL> alter database drop logfile group 3;
alter database drop logfile group 3
*
ERROR at line 1:
ORA-01623: log 3 is current log for instance orcl (thread 1) - cannot drop
ORA-00312: online log 3 thread 1:
'/home/oracle/app/oracle/oradata/ORCL/onlinelog/o1_mf_3_6k5t477n_.log'
ORA-00312: online log 3 thread 1:
'/home/oracle/app/oracle/flash_recovery_area/ORCL/onlinelog/o1_mf_3_6k5t49tq_.lo
g'(组3你是删不掉的)
select group#,members,status from v$log;
GROUP# MEMBERS STATUS
3 2 CURRENT
4 1 UNUSED
5 1 UNUSED
先将当前的日志切换到新创建的日志组成员上再删
SQL> alter system switch logfile;
select group#,members,status from v$log;
GROUP# MEMBERS STATUS
3 2 ACTIVE
4 1 CURRENT
5 1 UNUSED(当前日志已经切换到了组4上,组3变成了ACTIVE,这个时候不能贸然把它删除,因为ACTIVE意味着RBA指针还在它上面,RBA指针就是Redo Byte Address…先说两句,凡是位于RBA指针之上的日志,意味着他所对应的数据块已经写入硬盘了……一句两句讲不清楚,敬请期待接下来的备份恢复课程)
发一个检查点,将RBA指针移动到当前日志组上,因为我们即将把组3删除。
SQL> alter system checkpoint;
System altered.
select group#,members,status from v$log;
GROUP# MEMBERS STATUS
3 2 INACTIVE
4 1 CURRENT
5 1 UNUSED
(组3变成INACTIVE了)
SQL> alter database drop logfile group 3;
alter database drop logfile group 3
*
ERROR at line 1:
ORA-30036: unable to extend segment by 8 in undo tablespace 'UNDOTBS3'(报了一个错,但还是删掉了,估计删除日志也有一定的
回滚信息,其实回滚信息都会有的,不知道你能不能理解)
select group#,members,status from v$log;
GROUP# MEMBERS STATUS
4 1 CURRENT
5 1 UNUSED
减小了日志组的大小,还有一个参数别忘了,那就是FAST_START_MTTR_TARGET
SQL> show parameter fast
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
fast_start_io_target integer 0
fast_start_mttr_target integer 0
fast_start_parallel_rollback string LOW
SQL> alter system set fast_start_mttr_target=1;
System altered.
SQL> conn hr/hr
ERROR:
ORA-00604: error occurred at recursive SQL level 1
ORA-01552: cannot use system rollback segment for non-system tablespace 'USERS'
ORA-06512: at line 2
Warning: You are no longer connected to ORACLE.
(这是我碰到的错误,你自己做实验,最好先DROP LOGFILE再去将UNDOTBS减小,回滚段已经小到不够普通用户的连接了,因为正如我之前验证过的,登入一个SESSION会给你个指针指向给你分配的回滚区,USER使用的UNDOTBS3回滚区太小,自然就登不进来了,看到错误其实也是种好处,我反过来了,所以你自己做这个实验最好先DROP LOGFILE再将UNDO表空间减小)以上文字不明白没事,继续往下看。
再把之前建UNDO表空间的步骤再做一遍,如果你先删日志再将UNDO减小,就不用这么麻烦了。我写出来一是我懒得再改前面写过的了,二是这样也让你印象深刻,能明白本质的东西。
SQL> conn / as sysdba
Connected.
Create undo tablespace undotbs4 datafile size 512k;
Alter system set undo_tablespace=’UNDOTBS4’;
Drop tablespace undotbs3;
Alter tablespace undotbs4 retention guarantee;
Conn hr/hr (就可以进来了,当然你不用HR,用SYS去SELECT HR的表,也不是什么问题,只要你能做出以下的实验,并看到那个著名
的ORA-01555就行了!)—事后证明,最好先删日志再建UNDO
至此,准备工作完成!开始执行脚本。
我们先发个SELECT 语句(HR_SUNDO.SQL),再去发UPDATE语句(HR_UUNDO.SQL)。
再准备一个终端du –sh (临时表空间的数据文件,你会发现它不断增大)
在一个终端
Conn hr/hr
@/home/oracle/hr_sundo.sql(先)
在另一个终端
Conn hr/hr
@/home/oracle/hr_uundo.sql(后)
……
[oracle@ocpdb datafile]$ du -sh o1_mf_temp_6k5t4ox6_.tmp
9.1G o1_mf_temp_6k5t4ox6_.tmp(慢慢变大)
……
漫长的等待,可以先去泡个茶~上上网~或者是打开CCTV,看看欧洲冠军,国际米兰队的比赛~
ERROR at line 1:
ORA-01555: snapshot too old: rollback segment number 13 with name "_SYSSMU13$"
too small(在HR_UUNDO.SQL脚本那会看到这个错误)
select segment_name,tablespace_name from dba_rollback_segs;
SEGMENT_NAME TABLESPACE_NAME
SYSTEM SYSTEM
_SYSSMU11$ UNDOTBS2
_SYSSMU12$ UNDOTBS2
_SYSSMU13$ UNDOTBS2
在那个时候,TRANSACTION位于_SYSSMU13$段上,被清掉了,自然就SELECT不到,就报ORA-01555错误了。
四: 如何查询UNDO表空间的使用量
(1) UNDO表空间的总大小
select sum(bytes)/1024/1024 "Toltal M" from dba_data_files d where d.tablespace_name='UNDOTBS1';
Toltal M
90
(2) 正在使用的UNDO表空间的用量
select sum(bytes)/1024/1024 "undo used M" from dba_undo_extents e where e.status!='EXPIRED';
undo used M
.25
做个实验,当有TRANSACTION发生时,正在使用的UNDO表空间的量就会增大
@/home/oracle/hr_uundo.sql
select sum(bytes)/1024/1024 "undo used M" from dba_undo_extents e where e.status!='EXPIRED';
undo used M
8.125 (增大了)
五: 当有TRANSACTION发生时,热的切换UNDO表空间
SQL> show parameter undo_tablespace
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_tablespace string UNDOTBS1
SQL> create undo tablespace undotbs2 datafile size 10m;
Tablespace created.
SQL> conn hr/hr(请用普通人来做,SYS做会不一样)
Connected.
SQL> update hr.hr_tundo set uvalue=6 where uname='a5';
1 row updated.
这个时候的旧值在UNDOTBS1上
SQL> alter system set undo_tablespace='UNDOTBS2';
System altered.
SQL> show parameter undo_tablespace
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_tablespace string UNDOTBS2
这个时候我想删除UNDOTBS1,删的掉吗?
SQL> drop tablespace undotbs1;
drop tablespace undotbs1
*
ERROR at line 1:
ORA-30013: undo tablespace 'UNDOTBS1' is currently in use
(之前的TRANSACTION还在其上面)
在UPDATE那边
SQL> commit;
Commit complete.
(COMMIT后,它开始倒计时,900,899,898…,等它彻底消隐掉,我们才能删除UNDOTBS1,明白?)
SQL> drop tablespace undotbs1;
Tablespace dropped. (可以了)
注意: UNDOTBS1与UNDOTBS2必须在热的时候切换,也就是在数据库OPEN状态下切换,不能在NOMOUNT与MOUNT下切换,否则你改完之后,下次数据库就起不来了。(这里不讨论隐含参数的恢复)
六: 附加(仅在测试环境可用,禁止生产环境进行此等操作,除非你认为你是盖叔)
我们经常会有这样的情况发生,数据库SHUTDOWN ABORT后,回滚段丢失(因为本章是讲回滚表空间,自然例子就举回滚表空间的例子,请不要认为一定只有回滚表空间会丢失!)通过修改隐含参数,来达到打开数据库的目的。(再次声明,本实验只能在你自己的测试环境下做!)
[oracle@ocpdb datafile]$ rm -rf o1_mf_undotbs1_6k5t18td_.dbf
[oracle@ocpdb datafile]$ pwd
/home/oracle/app/oracle/oradata/ORCL/datafile
SQL> startup
ORACLE instance started.
Total System Global Area 452984832 bytes
Fixed Size 1219952 bytes
Variable Size 104858256 bytes
Database Buffers 343932928 bytes
Redo Buffers 2973696 bytes
Database mounted.
ORA-01157: cannot identify/lock data file 2 - see DBWR trace file
ORA-01110: data file 2:
'/home/oracle/app/oracle/oradata/ORCL/datafile/o1_mf_undotbs1_6k5t18td_.dbf'
SQL> shutdown immediate
ORA-01109: database not open
Database dismounted.
ORACLE instance shut down.
SQL> create pfile from spfile;
File created.
[oracle@ocpdb dbs]$ pwd
/home/oracle/app/oracle/product/10.2.0/db_1/dbs
[oracle@ocpdb dbs]$ vi initorcl.ora
加入这一行
*._allow_resetlogs_corruption='TRUE'
SQL> startup pfile='/home/oracle/app/oracle/product/10.2.0/db_1/dbs/initorcl.ora';
ORACLE instance started.
Total System Global Area 452984832 bytes
Fixed Size 1219952 bytes
Variable Size 104858256 bytes
Database Buffers 343932928 bytes
Redo Buffers 2973696 bytes
Database mounted.
ORA-01157: cannot identify/lock data file 2 - see DBWR trace file
ORA-01110: data file 2:
'/home/oracle/app/oracle/oradata/ORCL/datafile/o1_mf_undotbs1_6k5t18td_.dbf'
SQL> select open_mode from v$database;
OPEN_MODE
----------
MOUNTED
SQL> select file#||' '||name||' '||status from v$datafile;
FILE#||''||NAME||''||STATUS
--------------------------------------------------------------------------------
2 /home/oracle/app/oracle/oradata/ORCL/datafile/o1_mf_undotbs1_6k5t18td_.dbf ONL
INE
(查出UNDO表空间所对应的数据文件号是2)
SQL> alter database datafile 2 offline drop;
Database altered.
select file#||' '||name||' '||status from v$datafile;
2 /home/oracle/app/oracle/oradata/ORCL/datafile/o1_mf_undotbs1_6k5t18td_.dbf OFF
LINE
(将它OFFLINE)
SQL> alter database open;
Database altered.
SQL> show parameter undo
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_management string AUTO
undo_retention integer 900
undo_tablespace string UNDOTBS1
select segment_name,status from dba_rollback_segs s where s.tablespace_name='UNDOTBS1';
SEGMENT_NAME STATUS
_SYSSMU1$ OFFLINE
_SYSSMU2$ OFFLINE
_SYSSMU3$ OFFLINE
_SYSSMU4$ OFFLINE
_SYSSMU5$ OFFLINE
_SYSSMU6$ OFFLINE
_SYSSMU7$ OFFLINE
_SYSSMU8$ OFFLINE
_SYSSMU9$ OFFLINE
_SYSSMU10$ OFFLINE
SQL> drop tablespace undotbs1;
drop tablespace undotbs1
*
ERROR at line 1:
ORA-30013: undo tablespace 'UNDOTBS1' is currently in use
(删不掉)
SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
Vi initorcl.ora
*.undo_management='MANUAL'
*.undo_tablespace='UNDOTBS1'
*.user_dump_dest='/home/oracle/app/oracle/admin/orcl/udump'
*._allow_resetlogs_corruption='TRUE'
*._corrupted_rollback_segments='_SYSSMU1$','_SYSSMU2$','_SYSSMU3$','_SYSSMU4$','
_SYSSMU5$','_SYSSMU6$','_SYSSMU7$','_SYSSMU8$','_SYSSMU9$','_SYSSMU10$'
*._offline_rollback_segments='TRUE'
(不是说都是把所有的段_SYSSMU1$........_SYSSMU10$全部写进去,看具体的情况,你如果只坏了某一个段,加进去某一个段即可)
SQL> startup pfile='/home/oracle/app/oracle/product/10.2.0/db_1/dbs/initorcl.ora';
ORACLE instance started.
Total System Global Area 452984832 bytes
Fixed Size 1219952 bytes
Variable Size 104858256 bytes
Database Buffers 343932928 bytes
Redo Buffers 2973696 bytes
Database mounted.
Database opened.
SQL> drop tablespace undotbs1;
Tablespace dropped.
(UNDOTBS1可以删了)
SQL> show parameter undo
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_management string MANUAL
undo_retention integer 900
undo_tablespace string UNDOTBS1
(这里还是UNDOTBS1,要新建一个UNDO表空间把它替换掉了)
SQL> create undo tablespace undotbs2 datafile size 10m;
Tablespace created.
SQL> alter system set undo_tablespace='UNDOTBS2';
alter system set undo_tablespace='UNDOTBS2'
*
ERROR at line 1:
ORA-02097: parameter cannot be modified because specified value is invalid
ORA-30014: operation only supported in Automatic Undo Management mode
(首先,你现在是用PFILE打开的库,不允许ALTER SYSTEM,并且你现在的UNDO被改成了MANUAL(手工)管理了。)
编辑INITORCL.ORA
*.undo_management='AUTO'
*.undo_tablespace='UNDOTBS2'
#*._allow_resetlogs_corruption='TRUE'
#*._corrupted_rollback_segments='_SYSSMU1$','_SYSSMU2$','_SYSSMU3$','_SYSSMU4$','_SYSSMU5$','_SYSSMU6$','_SYSSMU7$','_SYSSMU8$','_SYSSMU9$','_SYSSMU10$'
#*._offline_rollback_segments='TRUE'
(并且把以上隐含参数都注释掉)
SQL> create spfile from pfile;
File created.
(建议,你做以上这步之前先备份SPFILE)
SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup
ORACLE instance started.
Total System Global Area 452984832 bytes
Fixed Size 1219952 bytes
Variable Size 104858256 bytes
Database Buffers 343932928 bytes
Redo Buffers 2973696 bytes
Database mounted.
Database opened.
SQL> show parameter undo
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
undo_management string AUTO
undo_retention integer 900
undo_tablespace string UNDOTBS2
select segment_name,status from dba_rollback_segs s where s.tablespace_name='UNDOTBS2';
SEGMENT_NAME STATUS
_SYSSMU1$ ONLINE
_SYSSMU2$ ONLINE
_SYSSMU3$ ONLINE
_SYSSMU4$ ONLINE
_SYSSMU5$ ONLINE
_SYSSMU6$ ONLINE
_SYSSMU7$ ONLINE
_SYSSMU8$ ONLINE
_SYSSMU9$ ONLINE
_SYSSMU10$ ONLINE
_SYSSMU11$ OFFLINE
_SYSSMU12$ OFFLINE
SQL> drop table test_employees;
Table dropped.
SQL> create table test_employees as select * from employees;
Table created.
SQL> insert into test_employees select * from employees;
107 rows created.
SQL> commit;
Commit complete.
SQL> show user
USER is "HR"
(UNDO段可以正常使用,没问题)
但是你这样打开的数据库已经是不一致的了,因为你是强制打开的,很可能以后会有后遗症,所以强烈建议你将此库EXP出来,再DBCA新建一个库,再IMP进去!强烈建议!
回滚段丢失恢复方法总结:
1.
设置参数 *._allow_resetlogs_corruption=true 2.启动数据库到mount状态,将undo的数据文件offline drop; 3.打开数据库. 4.查询获取原来undo空间的segments. 5.如果回滚段不能直接删除的话,需要修改初始化参数. *._corrupted_rollback_segments=(_SYSSMU11$,_SYSSMU12$,_SYSSMU13$,_SYSSMU14$,_SYSSMU15$,_SYSSMU16$,_SYSSMU1
7$,_SYSSMU18$,_SYSSMU19$,_SYSSMU20$) *.undo_management='MANUAL' *._offline_rollback_segments=true 6.重新打开数据库,删除坏的回滚表空间. 7.新建回滚表空间. 8.导出数据,重新建库.
2. 生产环境禁止发此种操作,仅供测试参考。
原创粉丝点击