关于重做记录的结构 - NinGoo

本文深入解析Oracle数据库备份恢复过程中的重做记录结构及其作用。详细介绍了重做记录如何记录数据块和回滚块的修改,解释了为何需要记录回滚段头的修改向量,并阐述了实例恢复过程中重做记录的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

导读:

  以前一直对备份恢复理解得不是特别清楚。Oracle文档上一般都是说通过应用重做记录前滚,通过应用undo记录回滚,最终使数据库达到一致。又说,redo记录中包含了更改后的数据和更改前的数据,undo记录包含了更改前的记录,而且undo的更改也会记录redo,这没错,但是究竟redo中记录了哪些东西?对同一个更改,既然redo本身记录了前映象,为什么还要有undo记录前映象?为什么undo的变更也要记录redo?还有哪些东西需要记录redo?

  这些问题一直没有弄得太明白,前两天翻《Oracle8i备份与恢复手册》的时候,发现了宝贝:)里面讲到的重做记录的结构,对于理解这些问题大有帮助。在恢复的过程中,是以重做记录为单位的,要么应用整条重做记录,要么一条重做记录都不应用。但重做记录本身,也是有一组修改向量组成的。所谓的修改向量,就是对某个单独的块的改动记录。比如下面的SQL:

  update test set id=2 where department=100;

  就会产生三个修改向量:

  1) test表中department=100的记录所在的数据块被修改,需要在redo中记录该数据块的修改向量

  2)修改该数据块会产生一个回滚块,该回滚块记录了原来id的值,需要在redo中记录该回滚块的修改向量

  3)如果这是该事务的第一个改动,还会修改回滚段头的事务表,对回滚段头的修改也有一个修改向量记录

  这样,这个重做记录就包含三个修改向量。当然,有个时候一个重做记录可能包含更多的修改向量,比如产生行链接和行迁移时,改动的数据块就不止一个,对应的回滚块也不止一个,由于每个块都记录一个修改向量,这样一个重做记录中就包含了很多的修改向量。另外,从上面也可以看出,redo是通过记录undo的变更来记录数据块的前映象的,并不是单独在redo中记录数据的前映象和后映象,在undo中记录前映象,再在redo中记录undo的后映象。一个重做记录就包含了一次修改所涉及到的所有块,包括数据块,回滚块和回滚段头块等。所以在应用重做记录的时候,要么这个重做记录中的修改向量都应用成功,要么都应用失败,这和事务的概念是差不多的。

  那么为什么要记录回滚段头的修改向量呢?因为我们需要回滚段头的事务信息来判断一个事务是已经提交了还是未提交的。虽然在每个块中也有事务记录,但是块中的记录可能是不正确的。这里需要提到一个块清除(block cleanout)的概念。一般情况下,一个事务在commit时,会将该事务所有的重做记录写入到redo文件,会将回滚段头的事务信息清除,也会清除每个块中的事务标志。但是下面两种情况下,此时不会执行块清除

  1) 该事务commit前,某些已修改但未提交的块已经写入到datafile了

  2) 该事务修改的块的数量超过了buffer cache中所有块数的10%了

  这时,事务已经commit,回滚段头中的事务信息也清除了,但这些块中的事务标志还在。这些块会在下一次读取到的时候执行块清除动作。所以,如果只通过块中的事务标志来判断该事务有没有提交,就会出现误判的情况。

  理解了重做记录的结构后,再来看一下实例恢复的过程。在实例恢复开始时,会从redo日志中找出上一次的checkpoint点,从该点起开始应用redo前滚。在前滚过程中,每条重做记录都记录了数据块和对应回滚块的修改,有时也有回滚段头的修改。那么在应用重做记录后,就会重新构造出数据块,回滚块和回滚段头。就和最初对这些块的改动一样。在前滚完成后,buffer cache中的所有记录就和上次instance崩溃时一样了,除了instance崩溃时redobuffer中一些没有来得及写入redo文件的记录,由于提交成功的事务的redo一定已经写入redo文件了,那么这些没有写入redo的一定都是未提交的事务。由于instance崩溃前存在一些未提交记录,而这些未提交事务在重新启动instance并执行实例恢复后,显然不可能再继续下去了,所以需要回滚这些事务。回滚时从回滚段头读取事务信息,然后通过应用回滚段的记录来回滚事务。由于回滚块也是在buffer cache中修改的,也可能在实例崩溃时没有来得及写入到磁盘中,所以前面前滚时需要应用重做记录中对回滚块的修改向量重新构造出这些没有写入文件中的回滚块,这样后面的回滚阶段才会有所有必须的回滚块记录来进行回滚。



本文转自

http://blog.youkuaiyun.com/NinGoo/archive/2006/04/27/693804.aspx
<think>首先,用户的问题是:“我想用户想知道如何编写Perl的包(模块) 请问如何编写Perl的模块或包”。这直接来自引用内容,我需要基于提供的引用回答如何编写Perl模块。 系统级指令: - 所有行内数学表达式用$...$格式。但Perl模块是编程主题,可能没有数学表达式,所以我可以忽略这条,除非有需要。 - 独立公式用$$...$$,但同样,可能不适用。 - LaTeX语法正确:在回答中,如果有代码,我会用代码块,但指令说行内数学表达式用$...$,所以如果提到变量或方程,我会用,但Perl模块更多是代码。 - 使用中文回答:整个回答必须用中文。 - 生成相关问题:在回答后,添加相关问题部分。 - 引用标识:在回答中引用的段落末尾添加引用标识,如[^1]。 用户可见层: - 回答结构清晰,逐步解决问题。 - 保证真实可靠:基于提供的引用,因为引用是站内引用的。 提供的引用: - [^1]: Perl 包和模块的基本概念。 - [^2]: 创建Perl包的方法,示例代码。 - [^3]: 另一个简单模块示例。 - [^4]: 编写Perl模块的基础知识。 - [^5]: 如何安装Perl模块。 用户的问题是编写Perl模块,所以我需要从引用中提取相关信息。 关键点来自引用: - 引用[^2]: Perl包存储在文件中,文件名以.pm结尾。例如,包Mymodule存储在Mymodule.pm中。代码示例展示了如何定义包,使用Exporter来导出子程序和变量。 - 引用[^3]: 另一个示例,定义了包NinGoo,使用Exporter导出函数。 - 引用[^4]: 解释了包的概念,类似于文件夹,包含变量和子程序。还提到require、use、Exporter等。 回答结构: 1. 介绍Perl模块的基本概念。 2. 逐步解释如何编写一个简单的Perl模块。 3. 提供代码示例。 4. 解释关键部分,如Exporter的使用。 5. 添加相关问题。 由于指令要求使用中文,我会全程用中文。 添加引用:在回答中,当引用特定部分时,在末尾添加[^n]。 生成相关问题:在最后添加§§相关问题§§部分。 最终回答格式: - 清晰的标题或开头。 - 分步解释。 - 代码块(用```包围)。 - 结尾相关问题。</think>### 如何编写Perl模块(包) Perl模块(也称为包)是一种封装代码的机制,用于组织变量、子程序和相关功能,便于代码用和维护。Perl模块存储在.pm文件中,并通过`package`关键字定义。核心工具是`Exporter`模块,它允许导出函数和变量供外部使用。下面我将基于引用内容,逐步解释编写Perl模块的方法,确保内容真实可靠。 #### 步骤1: 理解基本概念 - **包(Package)**:Perl将变量和子程序存储在一个符号表中,包就是这个符号表的命名空间。包类似于“文件夹”,用于隔离代码[^4]。 - **模块(Module)**:一个.pm文件就是一个模块,文件名与包名相同(例如,包`Mymodule`对应文件`Mymodule.pm`)[^2]。 - **导出机制**:使用`Exporter`模块导出函数或变量,使其他程序能通过`use`语句访问它们。关键变量包括: - `@ISA`:继承自`Exporter`,启用导出功能。 - `@EXPORT`:默认导出的符号列表。 - `@EXPORT_OK`:可选导出的符号列表,需显式导入[^2][^3]。 #### 步骤2: 创建模块文件 1. **文件命名**:以包名命名文件,扩展名为`.pm`。例如,包名为`MyModule`,则文件为`MyModule.pm`。 2. **基本结构**:模块文件必须包含: - `package`声明定义包名。 - 使用`Exporter`模块处理导出。 - 定义子程序或变量。 - 以`1;`结尾,表示模块加载成功[^2][^3]。 #### 步骤3: 编写示例模块 以下是一个简单模块的完整代码示例,基于引用[^3]和[^2]修改,展示如何定义包、导出函数和变量。 ```perl #!/usr/bin/perl package MyModule; # 定义包名 use strict; use warnings; require Exporter; # 引入Exporter模块 our @ISA = qw(Exporter); # 继承Exporter our @EXPORT = qw(public_func); # 默认导出的函数 our @EXPORT_OK = qw($my_var); # 可选导出的变量 # 定义变量 our $my_var = 10; # 私有函数(未导出,仅内部使用) sub private_func { print "这是私有函数\n"; } # 公共函数(导出供外部调用) sub public_func { private_func(); # 调用私有函数 print "Hello, 世界!\n"; return $my_var; # 使用变量 } 1; # 模块必须以1结尾,表示成功加载 __END__ ``` #### 步骤4: 关键解释 - **导出控制**: - `@EXPORT`中的符号(如`public_func`)在`use MyModule;`时自动导入。 - `@EXPORT_OK`中的符号(如`$my_var`)需显式导入:`use MyModule qw($my_var);`[^2][^3]。 - **封装性**:未导出的函数(如`private_func`)只能在模块内部使用,提高代码安全性[^4]。 - **模块加载**:其他程序通过`use MyModule;`加载模块。Perl在`@INC`路径中查找.pm文件[^4]。 #### 步骤5: 使用模块 在另一个Perl脚本中调用模块: ```perl #!/usr/bin/perl use strict; use warnings; use MyModule; # 加载模块,自动导入public_func public_func(); # 调用导出的函数 # 可选导入变量:use MyModule qw($my_var); print $my_var; ``` #### 注意事项 - **文件路径**:确保模块文件位于`@INC`包含的路径中(如当前目录或Perl库路径)[^4]。 - **最佳实践**:使用`use strict;`和`use warnings;`避免错误,并合理设计导出列表以减少命名冲突[^3][^4]。 - **调试**:如果加载失败,检查文件权限和路径。错误信息可通过`perl -c MyModule.pm`验证语法[^5]。 通过合理地使用包和模块,您可以提高代码的可读性、可维护性和可复用性[^1]。如果您需要安装现有模块(如Tk),请参考Perl包管理工具(如CPAN或PPM)[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值