Undo就是用来记录保存事务操作过程中的数据,如果事务发生错误,可以之前的数据进行填补。

Undo segment 是保存在表空间上的。Undo 大小是固定的,既然是固定的也就是有限的。如果保存的记录非常多,那么它就会被占满,新记录的数据会覆盖掉最早的数据。

在Oracle数据库中,undo主要有三大作用:提供一致性读(Consistent Read)、回滚事务(Rollback
Transaction)以及实例恢复(Instance Recovery)

一致性读是相对于脏读(DirtyRead)而言的。

A事务读取B事务尚未提交的更改数据,并在这个数据的基础上操作。如果恰巧B事务回滚,那么A事务读到的数据时不被承认的。

假设某个表T中有10000条记录,获取所有记录需要15分钟时间。

当时间为9点整,某用户A发出一条查询语句:select * from T,该语句在9点15分时执行完毕。当用户A执行该SQL语句到9点10分的时候,另外一个用户B发出了一条delete命令,将T表中的最后一条记录删除并提交了。那么到9点15分时,A用户将返回多少条记录?

如果返回9999条记录,则说明发生了脏读;如果仍然返回10000条记录,则说明发生了一致性读。很明显,在
9点钟那个时间点发出查询语句时,表T中确实有10000条记录,只不过由于I/O的相对较慢,所以才会花15分钟完成所有记录的检索。对于Oracle
数据库来说,没有办法实现脏读,必须提供一致性读,并且该一致性读是在没有阻塞用户的DML的前提下实现的。

那么undo数据是如何实现一致性读的呢?还是针对上面的例子。用户A在9点发出查询语句时,服务器进程会将9
点那个时间点上的SCN号记录下来,假设该SCN号为SCN9.00。那么9点整的时刻的SCN9.00一定大于等于记录在所有数据块头部的ITL槽中的
SCN号(如果有多个ITL槽,则为其中最大的那个SCN号)。

注:
ITL(Interested Transaction
List)是Oracle数据块内部的一个组成部分,用来记录该块所有发生的事务,一个itl可以看作是一个记录,在一个时间,可以记录一个事务(包括提交或者未提交事务)。当然,如果这个事务已经提交,那么这个itl的位置就可以被反复使用了,因为itl类似记录,所以,有的时候也叫itl槽位。

服务器进程在扫描表T的数据块时,会把扫描到的数据块头部的ITL槽中的SCN号与SCN9:00之间进行比较,哪个更大。如果数据块头部的SCN号比SCN9.00要小,则说明该数据块在9点以后没有被更新,可以直接读取其中的数据;否则,如果数据块ITL槽的SCN号比SCN9.00要大,则说明该数据块在9点以后被更新了,该块里的数据已经不是9点那个时间点的数据了,于是要借助undo块。

9点10分,B用户更新了表T的最后一条记录并提交(注意,在这里,提交或者不提交并不是关键,只要用户B更新了表T,用户A就会去读undo数据块)。
假设被更新记录属于N号数据块。那么这个时候N号数据块头部的ITL槽的SCN号就被改为SCN9.10。当服务器进程扫描到被更新的数据块(也就是N号块)时,发现其ITL槽中的SCN9.10大于发出查询时的SCN9.00,说明该数据块在9点以后被更新了。
于是服务器进程到N号块的头部,找到SCN9.10所在的ITL槽。
由于ITL槽中记录了对应的undo块的地址,于是根据该地址找到undo块,
将undo块中的被修改前的数据取出,再结合N号块里的数据行,从而构建出9点10分被更新之前的那个时间点的数据块内容,这样的数据块叫做CR块(ConsistentRead)。
对于delete来说,其undo信息就是insert,也就是说该构建出来的CR块中就插入了被删除的那条记录。随后,服务器进程扫描该CR块,从而返回正确的10000条记录。

让我们继续把问题复杂化。假设在9点10分B用户删除了最后一条记录并提交以后,紧跟着9点11分,C用户在同一个数据块里(也就是N号块)插入了2条记录。这个时候Oracle又是如何实现一致性读的呢(假设表T的initrans为1,也就是只有一个ITL槽)?
因为我们已经知道,事务需要使用ITL槽,只要该事务提交或回滚,该ITL槽就能够被重用。换句话说,该ITL槽里记录的已经是SCN9.11,而不是SCN9.10了。\这时,ITL槽被覆盖了,Oracle的服务器进程又怎能找回最初的数据呢?

其中的秘密就在于,Oracle在记录undo数据的时候,不仅记录了改变前的数据,还记录了改变前的数据所在的数据块头部的ITL信息。
因此,9点10分B用户删除记录时(位于N号块里,并假设该N号块的ITL信息为[Undo_block0/SCN8.50]),
则Oracle会将改变前的数据(也就是insert)放到undo块(假设该undo块地址为Undo_block1)里,同时在该undo块里记录删除前ITL槽的信息(也就是[Undo_block0/ SCN8.50])。
删除记录以后,该N号块的ITL信息变为 [Undo_block1 /SCN9.10];到了9点11分,C用户又在N号块里插入了两条记录,
则Oracle将插入前的数据(也就是delete两条记录)放到undo块(假设该undo块的地址为Undo_block2)里,并将9点11分时的ITL槽的信息(也就是[Undo_block1/ SCN9.10])也记录到该undo块里。
插入两条记录以后,该N号块的ITL槽的信息改为 [Undo_block2 /SCN9.11]。

那么当执行查询的服务器进程扫描到N号块时,发现SCN9.11大于SCN9.00,于是到ITL槽中指定的
Undo_block2处找到该undo块。
发现该undo块里记录的ITL信息为[Undo_block1 /SCN9.10],
其中的SCN9.10仍然大于SCN9.00,
于是服务器进程继续根据ITL中记录的Undo_block1,找到该undo块。发现该undo块里记录的ITL信息为[Undo_block0/SCN8.50],这时ITL里的SCN8.50小于发出查询时的SCN9.00,
说明这时undo块包含合适的undo信息,于是服务器进程不再找下去,而是将N号块、Undo_block2以及Undo_block1的数据结合起来,构建CR块。将当前N号的数据复制到CR块里,然后在CR块里先回退9点11分的事务,也就是在CR块里删除两条记录,然后再回退9点10分的事务,也就是在CR块里插入被删除的记录,从而构建出9点钟时的数据。
Oracle就是这样,以层层嵌套的方式,查找整个undo块的链表,直到发现ITL槽里的SCN号小于等于发出查询时的那个SCN号为止。正常来说,当前undo块里记录的SCN号要比上一个undo块里记录的SCN号要小。
但是在查找的过程中,可能会发现当前undo块里记录的ITL槽的SCN号比上一个undo块里记录的SCN号还要大。这种情况说明由于事务被提交或回滚,导致当前找到的undo块里的数据已经被其他事务覆盖了,于是我们无法再找出小于等于发出查询时的那个时间点的SCN号,
这时Oracle就会抛出一个非常经典的错误——ORA-1555,也就是snapshot too old的错误。

回滚事务则是在执行DML以后,发出rollback命令撤销DML所作的变化。Oracle利用记录在ITL槽里记录的undo
块的地址找到该undo块,然后从中取出变化前的值,并放入数据块中,从而对事务所作的变化进行回滚。

实例恢复则是在SMON进程完成前滚并打开数据库以后发生。SMON进程会去查看undo
segment头部(所谓头部就是undo segment里的第一个数据块)记录的事务表(每个事务在使用undo块时,首先要在该undo块所在的undo
segment的头部记录一个条目,该条目里记录了该事务相关的信息,其中包括是否提交等),将其中既没有提交也没有回滚,而是在实例崩溃时被异常终止的事务全部回滚。

Oracle Undo与脏读解析的更多相关文章

  1. Oracle SQL的硬解析和软解析

    我们都知道在Oracle中每条SQL语句在执行之前都需要经过解析,这里面又分为软解析和硬解析.在Oracle中存在两种类型的SQL语句,一类为 DDL语句(数据定义语言),他们是从来不会共享使用的,也 ...

  2. Oracle undo 镜像数据探究

                                                                 Oracle undo 镜像数据探究  今天是2013-08-18,隔别一周的 ...

  3. Oracle undo我们需要掌握什么

    <Oracle undo我们需要掌握什么> 引言:undo 是Oracle数据库的重要组件,刚入门的朋友建议要把undo的原理和机制理解明白,尤其是和redo组件的区别和联系.了解undo ...

  4. Oracle数据库字符集问题解析

    Oracle数据库字符集问题解析 经常看到一些朋友问ORACLE字符集方面的问题,我想以迭代的方式来介绍一下.第一次迭代:掌握字符集方面的基本概念.有些朋友可能会认为这是多此一举,但实际上正是由于对相 ...

  5. Oracle undo 表空间管理 (摘DAVID)

    Oracle 的Undo有两种方式: 一是使用undo 表空间,二是使用回滚段. 我们通过 undo_management 参数来控制使用哪种方式,如果设为auto,就使用UNDO 表空间,这时必须要 ...

  6. Oracle sql执行计划解析

    Oracle sql执行计划解析 https://blog.csdn.net/xybelieve1990/article/details/50562963 Oracle优化器 Oracle的优化器共有 ...

  7. oracle undo redo 解析

    Undo是干嘛用的?          简单理解,就相当于Windows下的回收站.        你对数据执行修改时,数据库会生成undo信息,这样万一你执行的事务或语句由于某种原因失败了,或者如果 ...

  8. oracle undo回滚段详解

    1.Undo是干嘛用的?   在介绍undo之前先说一下另外一个东西 transaction ,翻译成交易或事务.我们在进行一个事务的过程中需要申请许多资源,一个复杂的事务也需要很多步来完成.那么一个 ...

  9. Oracle ORA-12154: TNS: 无法解析指定的连接标识符”错误

    主要原因: 1.监听服务没有起起来.windows平台个一如下操作:开始---程序---管理工具---服务,打开服务面板,启动oraclehome92TNSlistener服务. 2.database ...

随机推荐

  1. FluentData,它是一个轻量级框架,关注性能和易用性。

    http://www.cnblogs.com/zengxiangzhan/p/3250105.html FluentData,它是一个轻量级框架,关注性能和易用性. 下载地址:FlunenData.M ...

  2. intent属性

    private String mAction;private Uri mData;private String mType;private String mPackage;private Compon ...

  3. NSKeyedArchiver 类 格式

    结论:NSKeyedArchiver 以健值对+类名的形式保存数据: NSDictionary *tDic = @{@"kxxxx":@"hhhh",@&quo ...

  4. 操作haproxy配置文件教师版

    作用: 可查,可增,可删,可修改 #_*_coding:utf-8_*_ import os def file_handle(filename,backend_data,record_list=Non ...

  5. 用python写一个hello world程序

    1,http://www.python.org/download/ 下载windows安装包, 2,python环境变量配置 (1)设置环境变量:我的电脑-右键-属性-高级-环境变量 在Path中加入 ...

  6. 3DES封装类

    using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Se ...

  7. 如何用火车头采集当前页面url网址

    首先创建一个标签为本文网址,勾选后面的“从网址中采集”. 选择下面的“正则提取”,点击通配符“(?<content>?)”,这样在窗口中就显示为(?<content>[\s\S ...

  8. Charm Bracelet

    Charm Bracelet Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Subm ...

  9. JavaScript 最佳实践

    这个文档是基于JavaScript社区众多开发者的意见和经验,在开发JavaScript代码上的最佳实践和首选的方案的明细表.因为这是一个推荐的表而非原则性的方案,经验丰富的开发者可能对下面的表达会有 ...

  10. Unity3d LineRenderer画线

    原地址:http://www.cnblogs.com/88999660/archive/2013/01/21/2869498.html 1.  画多条线 画多条线需要在场景中放置多个GameObjec ...