oracle_专家高级编程_中文第九章.pdf
《oracle_专家高级编程_中文第九章.pdf》由会员分享,可在线阅读,更多相关《oracle_专家高级编程_中文第九章.pdf(38页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、 1第 9 章 redo 与 undo 这一章将介绍 Oracle 数据库中最重要的两部分数据:redo 与 undo。redo(重做信息)是 Oracle 在在线(或归档)重做日志文件中记录的信息,万一出现失败时可以利用这些数据来“重放”(或重做)事务。undo(撤销信息)是 Oracle 在 undo 段中记录的信息,用于取消或回滚事务。在这一章中,我们讨论的内容很多,包括 redo 和 undo(回滚信息)如何生成,以及事务、恢复等方面如何应用 redo 和 undo。首先我们给出一个高层概述,说明 undo 和 redo 分别是什么,它们如何协作。然后向下细化,更深入地介绍各个主题,并
2、讨论作为开发人员需要了解哪些内容。这一章主要面向开发人员,在此没有涵盖应由 DBA 完全负责确定和调整的问题。例如,如何找到 RECOVERY_PARALLELISM 或FAST_START_MTTR_TARGET 参数的最优设置?这个问题要由 DBA 确定,本章就没有涉及。但是,redo 和 undo 则是 DBA 和开发人员都关心的主题,它们是 DBA和开发人员之间的桥梁。不论是 DBA还是开发人员,都需要对 redo 和 undo 的作用有很好的基本了解,知道它们如何工作,并且知道如何避免与 redo 和 undo 的使用有关的潜在问题。如果掌握了 redo 和 undo 的相关知识,这
3、还有助于 DBA和开发人员更好地理解数据库一般如何操作。在这一章中,我将针对 Oracle 的这些机制提供伪代码,并从概念上解释到底会发生什么。这里不会详尽地介绍所有内部细节,如会用哪些数据字节更新哪些文件等详细内容并不会谈到。具体发生的情况可能比我们介绍的更复杂,但是不管怎样,如果能很好地理解这些机制的工作流程,将很有意义,这有助于理解你的动作会带来怎样的影响。9.1 什么是 redo?重做日志文件(redo log file)对 Oracle 数据库来说至关重要。它们是数据库的事务日志。Oracle 维护着两类重做日志文件:在线(online)重做日志文件和归档(archived)重做日志
4、文件。这两类重做日志文件都用于恢复;其主要目的是,万一实例失败或介质失败,它们就能派上用场。如果数据库所在主机掉电,导致实例失败,Oracle 会使用在线重做日志将系统恰好恢复到掉电之前的那个时间点。如果磁盘驱动器出现故障(这是一个介质失败),Oracle 会使用归档重做日志以及在线重做日志将该驱动器上的数据备份恢复到适当的时间点。另外,如果你“无意地”截除了一个表,或者删除了某些重要的信息,然后提交了这个操作,那么可以恢复受影响数据的一个备份,并使用在线和归档重做日志文件把它恢复到这个“意外”发生前的时间点。归档重做日志文件实际上就是已填满的“旧”在线重做日志文件的副本。系统将日志文件填满时
5、,ARCH 进程会在另一个位置建立在线重做日志文件的一个副本,也可以在本地和远程位置上建立多个另外的副本。如果由于磁盘驱动器损坏或者其他物理故障而导致失败,就会用这些归档重做日志文件来执行介质恢复。Oracle 拿到这些归档重做日志文件,并把它们应用于数据文件的备份,使这些数据文件能“赶上”数据库的其余部分。归档重做日志文件是数据库的事务历史。注意注意 随着 Oracle 10g 的到来,我们现在还有了一种闪回技术(flashback)。利用闪回技术,可以执行闪回查询(也就是说,查询过去某个时间点的数据),取消数据库表的删除,将表置回到以前某个时间的状态,等等。因此,现在使用备份和归档重做日志
6、文件来完成传统恢复的情况越来越少。不过,执行恢复是 DBA最重要的任务,而且 DBA在数据库恢复方面绝对不能犯错误。每个 Oracle 数据库都至少有两个在线重做日志组,每个组中至少有一个成员(重做日志文件)。这些在线重做日志组以循环方式使用。Oracle 会写组 1 中的日志文件,等写到组 1 中文件的最后时,将切换到日志文件组 2,开始写这个组中的文件。等到把日志文件组 2 写满时,会再次切换回日志文件组 1(假设只有两个重做日志文件组;如果有 3 个重做日志文件组,Oracle 当然会继续写第 3 个组)。数据库之所以成为数据库(而不是文件系统等其他事物),是因为它有自己独有的一些特征,
7、重做日志或事务日志就是其中重要的特性之一。重做日志可能是数据库中最重要的恢复结构,不过,如果没有其他部分(如 undo 段、分布式事务恢复等),但靠重做日志什么也做不了。重做日志是数据库区别于传统文件系统的一个主要因素。Oracle 正写到一半的时候有可能发生掉电,利用在线重做日志,我们就能有效地从这个掉电失败中恢复。归档重做日志则允许我们从介质失败中恢复,如硬盘损坏,或者由于人为错误而导致数据丢失。如果没有重做日志,数据库提供 id 保护就比文件系统多不了多少。29.2 什么是 undo?从概念上讲,undo 正好与 redo 相对。你对数据执行修改时,数据库会生成 undo 信息,这样万一
8、你执行的事务或语句由于某种原因失败了,或者如果你用一条 ROLLBACK 语句请求回滚,就可以利用这些 undo 信息将数据放回到修改前的样子。redo 用于在失败时重放事务(即恢复事务),undo 则用于取消一条语句或一组语句的作用。与 redo 不同,undo 在数据库内部存储在一组特殊的段中,这称为 undo段(undo segment)。注意注意 “回滚段”(rollback segment)和“undo 段“(undo segment)一般认为是同义词。使用手动 undo 管理时,DBA会创建”回滚段“。使用自动 undo 管理时,系统将根据需要自动地创建和销毁”undo 段“。对于
9、这里的讨论来说,这些词的意图和作用都一样。通常对 undo 有一个误解,认为 undo 用于数据库物理地恢复到执行语句或事务之前的样子,但实际上并非如此。数据库只是逻辑地恢复到原来的样子,所有修改都被逻辑地取消,但是数据结构以及数据库块本身在回滚后可能大不相同。原因在于:在所有多用户系统中,可能会有数十、数百甚至数千个并发事务。数据库的主要功能之一就是协调对数据的并发访问。也许我们的事务在修改一些块,而一般来讲往往会有许多其他的事务也在修改这些块。因此,不能简单地将一个块放回到我们的事务开始前的样子,这样会撤销其他人(其他事务)的工作!例如,假设我们的事务执行了一个INSERT语句,这条语句导
10、致分配一个新区段(也就是说,导致表的空间增大)。通过执行这个INSET,我们将得到一个新的块,格式化这个块以便使用,并在其中放上一些数据。此时,可能出现另外某个事务,它也向这个块中插入数据。如果要回滚我们的事务,显然不能取消对这个块的格式化和空间分配。因此,Oracle 回滚时,它实际上会做与先前逻辑上相反的工作。对于每个 INSERT,Oracle 会完成一个 DELETE。对于每个 DELETE,Oracle 会执行一个 INSERT。对于每个 UPDATE,Oracle 则会执行一个“反UPDATE“,或者执行另一个 UPDATE 将修改前的行放回去。注意注意 这种 undo 生成对于直
11、接路径操作(direct path operation)不适用,直接路径操作能够绕过表上的 undo 生成。稍后将更详细地讨论这些问题。怎么才能看到 undo 生成(undo generation)具体是怎样的呢?也许最容易的方法就是遵循以下步骤:(1)创建一个空表。(2)对它做一个全部扫描,观察读表所执行的 I/O 数量。(3)在表中填入许多行(但没有提交)。(4)回滚这个工作,并撤销。(5)再次进行全表扫描,观察所执行的 I/O 数量。首先,我们创建一个空表:ops$tkyteORA10G create table t 2 as 3 select*4 from all_objects 5
12、where 1=0;Table created.然后查询这个表,这里在 SQL*Plus 中启用了 AUTOTRACE,以便能测试 I/O。注意注意 在这个例子中,每次(即每个用例)都会做两次全表扫描。我们的目标只是测试每个用例中第二次完成的 I/O。这样可以避免统计在解析和优化期间优化器可能完成的额外 I/O。最初,这个查询需要 3 个 I/O 来完成这个表的全表扫描:ops$tkyteORA10G select*from t;no rows selected 3 ops$tkyteORA10G set autotrace traceonly statistics ops$tkyteORA1
13、0G select*from t;no rows selected Statistics-0 recursive calls 0 db block gets 3 consistent gets.ops$tkyteORA10G set autotrace off 接下来,向表中增加大量数据。这会使它“扩大“,不过随后再将其回滚:ops$tkyteORA10G insert into t select*from all_objects;48350 rows created.ops$tkyteORA10G rollback;Rollback complete.现在,如果再次查询这个表,会发现这一次读
14、表所需的 I/O 比先前多得多:ops$tkyteORA10G select*from t;no rows selected ops$tkyteORA10G set autotrace traceonly statistics ops$tkyteORA10G select*from t;no rows selected Statistics-0 recursive calls 0 db block gets 689 consistent gets.ops$tkyteORA10G set autotrace off 前面的 INSERT 导致将一些块增加到表的高水位线(high-water ma
15、rk,HWM)之下,这些块没有因为回滚而消失,它们还在那里,而且已经格式化,只不过现在为空。全表扫描必须读取这些块,看看其中是否包含行。这说明,回滚只是一个“将数据库还原“的逻辑操作。数据库并非真的还原成原来的样子,只是逻辑上相同而已。9.2.1 redo和 undo 如何协作?在这一节中,我们来看看在不同场景中 redo 和 undo 如何协作。例如,我们会讨论处理 INSERT 时关于 redo 和 undo 生成会发生什么情况,另外如果在不同时间点出现失败,Oracle 将如何使用执行信息。有意思的是,尽管 undo 信息存储在 undo 表空间或 undo 段中,但也会受到 redo
16、的保护。换句话说,会把 undo 数据当成是表数据或索引数据一样,对 undo 的修改会生成一些 redo,这些 redo 将计入日志。为什么会这样呢?稍后在讨论系统崩溃时发生的情况时将会解释它,到时你会明白了。将 undo 数据增加到 undo 段中,并像其他部分的数据一样在缓冲区缓存中得到缓存。INSERT-UPDATE-DELETE 示例场景示例场景 作为一个例子,我们将分析对于下面这组语句可能发生什么情况:4insert into t(x,y)values(1,1);update t set x=x+1 where x=1;delete from t where x=2;我们会沿着不同
17、的路径完成这个事务,从而得到以下问题的答案:q 如果系统在处理这些语句的不同时间点上失败,会发生什么情况?q 如果在某个时间点上 ROLLBACK,会发生什么情况?q 如果成功并 COMMIT,会发生什么情况?1.INSERT 对于第一条 INSERT INTO T 语句,redo 和 undo 都会生成。所生成的 undo 信息足以使 INSERT“消失“。INSERT INTO T 生成的 redo信息则足以让这个插入”再次发生“。插入发生后,系统状态如图 9-1 所示。图图 9-1 INSERT 之后的系统状态 这里缓存了一些已修改的 undo 块、索引块和表数据块。这些块得到重做日志缓
18、冲区中相应条目的“保护“。l 假想场景:系统现在崩溃假想场景:系统现在崩溃 即使系统现在崩溃也没有关系。SGA会被清空,但是我们并不需要 SGA里的任何内容。重启动时就好像这个事务从来没有发生过一样。没有将任何已修改的块刷新输出到磁盘,也没有任何 redo 刷新输出到磁盘。我们不需要这些 undo 或 redo 信息来实现实例失败恢复。l 假想场景:缓冲区缓存现在已满假想场景:缓冲区缓存现在已满 在这种情况下,DBWR 必须留出空间,要把已修改的块从缓存刷新输出。如果是这样,DBWR 首先要求 LGWR 将保护这些数据库块的 redo 条目刷新输出。DBWR 将任何有修改的块写至磁盘之前,LG
19、WR 必须先刷新输出与这些块相关的 redo 信息。这是有道理的如果我们要刷新输出表 T 中已修改的块,但没有刷新输出与 undo 块关联的 redo 条目,倘若系统失败了,此时就会有一个已修改的表 T 块,而没有与之相关的 redo 信息。在写出这些块之前需要先刷新输出重做日志缓存区,这样就能重做(重做)所有必要的修改,将 SGA 放回到现在的状态,从而能发生回滚。从第二个场景还可以预见到一些情况。这里描述的条件是“如果刷新输出了表 T 的块,但没有刷新输出 undo 块的相应 redo,而且此时系统失败了“,这个条件开始变得有些复杂。随着增加更多用户、更多的对象,再加上并发处理等因素,条件
20、还会更复杂。此时的情况如图 9-1 所示。我们生成了一些已修改的表和索引块。这些块有一些与之关联的 undo 段块,这 3 类块都会生成 redo 来保护自己。如果还记得第 4 章中对重做日志缓冲区的讨论,应该知道,它会在以下情况刷新输出:每 3 秒一次;缓冲区 1/3 满时或者包含了1MB 的缓冲数据;或者是只要发生提交就会刷新输出。重做日志缓冲区还有可能会在处理期间的某一点上刷新输出。在这种情况下,其状态如图 9-2 所示。5 图图 9-2 重做日志缓冲区刷新输出后的系统状态 2.UPDATE UPDATE 所带来的工作与 INSERT 大体一样。不过 UPDATE 生成的 undo 量更
21、大;由于存在更新,所以需要保存一些“前“映像。系统状态如图 9-3 所示。图图 9-3 UPDATE 后的系统状态 块缓冲区缓存中会有更多新的 undo 段块。为了撤销这个更新,如果必要,已修改的数据库表和索引块也会放在缓存中。我们还生成了更多的重做日志缓存区条目。下面假设前面的插入语句生成了一些重做日志,其中有些重做日志已经刷新输出到磁盘上,有些还放在缓存中。l 假想场景:系统现在崩溃假想场景:系统现在崩溃 启动时,Oracle 会读取重做日志,发现针对这个事务的一些重做日志条目。给定系统的当前状态,利用重做日志文件中对应插入的redo 条目,并利用仍在缓冲区中对应插入的 redo 信息,O
22、racle 会“前滚”插入。最后到与图 9-1 类似的状态。现在有一些 undo 块(用以撤销插入)、已修改的表块(刚插入后的状态),以及已修改的索引块(刚插入后的状态)。由于系统正在进行崩溃恢复,而且我们的会话还不再连接(这是当然),Oracle 发现这个事务从未提交,因此会将其回滚。它取刚刚在缓冲区缓存中前滚得到的 undo,并将这些 undo 应用到数据和索引块,使数据和索引块“恢复”为插入发生前的样子。现在一切都回到从前。磁盘上的块可能会反映前面的 INSERT,也可能不反映(这取决于在崩溃前是否已经将块刷新输出)。如果磁盘上的块确实反映了插入,而实际上现在插入已经被撤销,当从缓冲区缓
23、存刷新输出块时,数据文件就会反映出插入已撤销。如果磁盘上的块本来就没有反映前面的插入,就不用去管它这些块以后肯定会被覆盖。这个场景涵盖了崩溃恢复的基本细节。系统将其作为一个两步的过程来完成。首先前滚,把系统放到失败点上,然后回滚尚未提交 6的所有工作。这个动作会再次同步数据文件。它会重放已经进行的工作,并撤销尚未完成的所有工作。l 假想场景:应用回滚事务假想场景:应用回滚事务 此时,Oracle 会发现这个事务的 undo 信息可能在缓存的 undo 段块中(基本上是这样),也可能已经刷新输出到磁盘上(对于非常大的事务,就往往是这种情况)。它会把 undo 信息应用到缓冲区缓存中的数据和索引块
24、上,或者倘若数据和索引块已经不在缓存中,则要从磁盘将数据和索引块读入缓存,再对其应用 undo。这些块会恢复为其原来的行值,并刷新输出到数据文件。这个场景比系统崩溃更常见。需要指出,有一点很有用:回滚过程中从不涉及重做日志。只有恢复和归档时会当前重做日志。这对于调优是一个很重要的概念:重做日志是用来写的(而不是用于读)。Oracle 不会在正常的处理中读取重做日志。只要你有足够的设备,使得 ARCH 读文件时,LGWR 能写到另一个不同的设备,那么就不存在重做日志竞争。许多其他的数据库(非 Oracle)都把日志文件处理为“事务日志”。这些数据库没有把 redo 和 undo 分开。对于这些系
25、统,回滚可能是灾难性的,回滚进程必须读取日志,而日志写入器正在试图写这个日志。这就向系统中最薄弱的环节引入了竞争。Oracle 的目标是:可以顺序地写日志,而且在写日志时别人不会读日志。3.DELETE 同样,DELETE 会生成 undo,块将被修改,并把 redo 发送到重做日志缓冲区。这与前面没有太大的不同。实际上,它与 UPDATE如此类似,所以我们不再啰嗦,直接来介绍 COMMIT。4.COMMIT 我们已经看到了多种失败场景和不同的路径,现在终于到 COMMIT 了。在此,Oracle 会把重做日志缓冲区刷新输出到磁盘,系统状态如图 9-4 所示。图图 9-4 COMMIT 后的系
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- oracle_ 专家 高级 编程 中文 第九
限制150内