《 Oracle 的核心与技术 》 这本书说过,Oracle事务管理起源于Undo.
Oracle的默认事务隔离级别是read commit.但是这个级别与MySQL的 read commit又有些略有不同,但是大的原理方向上是差不多的。 Undo的最重要的一个作用就是存储数据被修改之前的数据,可以回滚事务。
当 一个表中的 2个Block 正好被修改,且没有提交, 这时候有1个查询进来,比对SCN,发现查询的时候的SCN是10023,那么这个SQL是不能查询SCN比10023大的10024的数据的(就好像人不能知道1分钟之后发生的事情),那就只能查询这2个 Block 离 10023 最近的 SCN 的数据,如图,分别是1008 和 10021。这是为了满足读一致性。
这2个被修改且没有提交的Block就被记录在undo 表空间(其实有很大的可能是在SGA中,还没有刷新到undo的物理磁盘中)。
undo 表空间的undo 段是可以被重复使用的,当修改的数据特别频繁,产生的undo 比较大的时候,10秒前的undo segment 可能就被最新1秒产生的undo 覆盖,这时候如果查询的时候需要查询10秒前的undo,就会报快照过旧的错误。 避免这种错误的产生无非就是 加大undo空间。
undo的相反就是redo,redo 记录的是oracle沿着SCN所做的一切改变。undo和redo的结合,保证了oracle 数据库可以做到数据的100%不丢失。当一个事务发生的时候,redo和undo是怎么配合的?
顺序:
1 生成undo的redo
2. 生成undo
3. 生成数据本身的redo
4. 修改数据
redo 本身也会记录undo的改变
undo对于数据的改变,有不同的记录方式 insert : undo 只记录插入记录的rowid; update: undo 只记录修改了的字段变化前的数据 delete: undo 记录了整个行的记录
同一个事物不能跨越回滚段 ,同一个回滚段至少有2个extent, 每一个回滚段头都有1个 block 记录了事务表信息。 每有一个新事物,就会在事物信息表上添加一行记录 .
(事务信息表:事物标志,事物状态,占用的回滚段块数等. 11G中 8K 可以记录34行 。 由于记录可以覆盖重用的关系,每次重用时,wrap# 数值会增加,并且重启 wrap#也不会重置。每个undo 段中的每个槽位都有自己的wrap#。 PS: 原则上来说,oracle 关于计数的数值,重启都不会重置)
undo 段虽然可以循环使用,但是依然会有扩展extent的情况出现当一个 undo 段中原本有 5 个extent, e1 e2 e3 e4 e5. 当中有个长事务一直 占用着 e3 ,当其他事务用完 e4 e5 循环到 e2,并且 e2也用完的时候,准备用e3发现e3上有其他的事务在占用,这时候的事务是不可以越过 e3 来使用 e4的, 这时候 e2 就会扩大extent 以满足事务的需求。当 e3 事务完结之后,才可正常循环e3 可以正常使用后, 扩大的 e2 并不是占用着扩大的空间,而是会缩小。 (缩小与否取决于optimal 参数,超过optimal 参数就会缩小,否则不会)
v$rollstat 视图可以查看 回滚段的扩大和缩小的次数
UNDOTBS1 这个平时看到的表空间就是我们平时事务所用的undo表空间, undo 段就存储在这里面, undo 段也是会加载到 SGA 里面的
另外还有数据库系统本身使用的undo 段, 在system 表空间中。系统回滚段只用于系统的使用 , 比如 create ,drop ,truncate 等操作的时候,对系统字典回滚记录。orace想要创建 普通的回滚段,则system 回滚段必须先有,且正常,否则不能创建
undo 出现的最多的问题就2个: 空间不足,回滚段个数太少(默认10个,自动关联模式的情况下,这个个数是会变化的,比10个只多不少)。
transactions=1689
transactions_per_rollback_segment= 5
回滚段的的个数和大小,直接和并发事务有关,若每个回滚段上 活动的事务太多, 会导致严重的回滚段争用。 因为回滚段的 可覆盖使用的关系, 瓶颈总是决定于最小的一个回滚段(木桶原理)。
创建回滚段
create public rollback segment your_rb_name tablespace RBS storage(initial 3M next 3M minextents 10 optimal 50M);
undo 表空间如果变得太大, 并且业务其实并不需要那么大的表空间。如果缩小不了,可以 新建一个新的 undo2表空间,将原本的undo 表空间删除掉。
alter system set undo_tablespace=undotbs2 scope=both;
v$waitstat 视图可以看下 是否要增大undo ;
PS:
事务ID 的 组成: undo段编号+undo事务表中条目索引号+最新的wrap#值
比如: 0x0009.002.00002013 。表明这个事务在 undo 9段,第2条记录, wrap# 值是 0x2013 (十进制8211)