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. 浅谈php对api开发的作用

    最近正在做一个手机APP的服务端API开发,虽然是基于Ruby on Rails的,做的也不太专业,不过大致相通,希望能够给你一些启发. 首先,如果是比较简单的手机APP,例如新闻客户端这样的不会涉及 ...

  2. C++中debug和release的区别 . 转载

    vc中debug和release的不同 收藏  在使用VC开发软件的过程中,正当要享受那种兴奋的时候突然发现:release与debug运行结果不一致,甚至出错,而release又不方便调试,真的是当 ...

  3. safeNet

    把那4个dll丢到C:\Windows\SysWOW64里去重启IIS,再测试

  4. ping 和 traceroute 命令

    ping 和 traceroute 命令 ping 程序 就是发送一个ICMP查询报文给某服务器,以测试该服务器是否可达. 当返回ICMP回显应答时,要打印出序列号.TTL,和往返时间:   [roo ...

  5. 最长公共子序列(LCS)

    简单的DP. f[i][j]表示序列a中前i个中,序列b中前b个中,组成的最长公共子序列的长度. DP方程: if(a[i-1]==b[j-1]) f[i][j]=f[i-1][j-1]+1;  el ...

  6. DOM: 如何获取元素下的第一个子元素

    Element.firstChild ?,是的,这是第一种方法,当然,通常来说支持 W3C 规范的浏览器,如 Firefox 等取到的应该是 TEXT_NODE.很早之前,或者说现在最流行的方法可能是 ...

  7. Dean Edwards大神写的addEvent库

    直接晒代码: // written by Dean Edwards, 2005 // with input from Tino Zijdel, Matthias Miller, Diego Perin ...

  8. Python配合BeautifulSoup读取网络图片并保存在本地

    本例为Python配合BeautifulSoup读取网络图片,并保存在本地. BeautifulSoup可代替正则表达式,更好地解析Html文本,获取其中的指定内容,如Tag.Property等 # ...

  9. localResizeIMG

    http://think2011.net/localResizeIMG/test/ 演示一下 自己试试 点我直接进入演示页面 说明 在客户端压缩好要上传的图片可以节省带宽更快的发送给后端,特别适合在移 ...

  10. How to install OpenResty

    How to install OpenResty 15 January 2014, 6:18 am   OpenResty, also called “ngx_openresty”, is a web ...