在oracle中我们做一些更新操作,oracle底层是怎么流转的呢,就是这篇文章要讲解的。
一. 原理
假设我们在一个已经更新了很多条分散记录的OLTP系统中,更新了一行数据。那么这个更新的真实步骤如下:
1. 创建一个重做改变向量,描述如何往undo块插入一条undo记录(也就是描述undo块的改变)
2. 创建一个重做改变向量,描述数据块的改变(也就是数据的改变)
3. 合并这两个重做改变向量为一条日志记录,并写到重做日志缓冲区(便于今后重做)
4. 向undo块插入undo记录(便于今后回退)
5. 改变数据块中的数据(这里才真正改变数据)
下面我们通过一个例子来展示这个过程。
二. 实践
我们先创建一个表,然后更新表中数据第一个块的第3,4,5,条记录,并且在每更新一条后会穿插更新第二个块的一条记录。也就是这个更新操作会更新6条记录,他会改变每一个记录的第三列------ 一个varchar2类型的字段,将其由xxxxxx(小写6个字符)改变为YYYYYYYYYY(大写10个字符)。
1. cmd命令行 以sys 用户登录
2. 准备工作(创建几个存储过程,用来转储块,转储重做日志等)
这些脚本见:http://download.csdn.net/detail/liwenshui322/7912909
3. 准备工作(主要清除回收站删除信息,设置块读取代价,估值计算依据等)
- start setenv
- set timing off
- execute dbms_random.seed(0)
- drop table t1;
- begin
- begin execute immediate 'purge recyclebin'; --清空回收站
- exception when others then null;
- end;
- begin
- dbms_stats.set_system_stats('MBRC',8); --多块读取为8块
- dbms_stats.set_system_stats('MREADTIM',26); --对块读取平均时间为26毫秒
- dbms_stats.set_system_stats('SREADTIM',12); --单块读取平均时间为30毫秒
- dbms_stats.set_system_stats('CPUSPEED',800); --cpu每秒可执行800,000,000个操作
- exception
- when others then null;
- end;
- begin execute immediate 'begin dbms_stats.delete_system_stats; end;'; --删除系统统计信息
- exception when others then null;
- end;
- begin execute immediate 'alter session set "_optimizer_cost_model"=io'; --基于io来计算估值
- exception when others then null;
- end;
- end;
- /
4. 创建表与索引
- create table t1
- as
- select
- 2 * rownum - 1 id,
- rownum n1,
- cast('xxxxxx' as varchar2(10)) v1,
- rpad('0',100,'0') padding
- from
- all_objects
- where
- rownum <= 60
- union all
- select
- 2 * rownum id,
- rownum n1,
- cast('xxxxxx' as varchar2(10)) v1,
- rpad('0',100,'0') padding
- from
- all_objects
- where
- rownum <= 60
- ;
- create index t1_i1 on t1(id);
5. 统计表信息
- begin
- dbms_stats.gather_table_stats(
- ownname => user,
- tabname =>'T1',
- method_opt => 'for all columns size 1'
- );
- end;
- /
6.查看表占用的块情况,和每一个块有多少条数据
- select
- dbms_rowid.rowid_block_number(rowid) block_number,
- count(*) rows_per_block
- from
- t1
- group by
- dbms_rowid.rowid_block_number(rowid)
- order by
- block_number
- ;
我们会看到,总共占用两个块,每一个块都有60条记录
7. 转储数据块
- alter system switch logfile;
- execute dump_seg('t1')
8. 做更新
- update
- /*+ index(t1 t1_i1) */
- t1
- set
- v1 = 'YYYYYYYYYY'
- where
- id between 5 and 9
- ;
9. 转储更新块之后的数据块和undo块(发生检查点语句执行后,下一个语句等5,6s再执行,发生检查点只是告诉oracle将脏数据写入磁盘,需要一点时间)
- pause Query the IMU structures now (@core_imu_01.sql)
- alter system checkpoint;--发生检查点,让数据写到磁盘
- execute dump_seg('t1')
- execute dump_undo_block
10. 转储redo块
- rollback;
- commit;
- execute dump_log
11. 定位转储信息文件位置
- select sid from v$mystat where rownum=1;--查询结果传入下一个sql
- SELECT d.value||'/'||lower(rtrim(i.instance, chr(0)))||'_ora_'||p.spid||'.trc' trace_file_name
- from
- ( select p.spid from v$session s, v$process p
- where s.sid='133' and p.addr = s.paddr) p,
- ( select t.instance from v$thread t,v$parameter v
- where v.name = 'thread' and (v.value = 0 or t.thread# = to_number(v.value))) i,
- ( select value from v$parameter where name = 'user_dump_dest') d;
12. 打开文件
下面看几个关键部分,我们看第一个块的第5条数据,我们将这一行数据的第三列由xxxxxx改成了YYYYYYYYYY。
update之前:
- tab 0, row 4, @0x1d3f
- tl: 117 fb: --H-FL-- lb: 0x0 cc: 4
- col 0: [ 2] c1 0a
- col 1: [ 2] c1 06
- col 2: [ 6] 78 78 78 78 78 78
- col 3: [100]
- 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
- 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
- 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
- 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
我们看到col2长度是6,然后是6个78(x的十六进制ASCII码是78)。
update之后:
- tab 0, row 4, @0x2a7
- tl: 121 fb: --H-FL-- lb: 0x2 cc: 4
- col 0: [ 2] c1 0a
- col 1: [ 2] c1 06
- col 2: [10] 59 59 59 59 59 59 59 59 59 59
- col 3: [100]
- 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
- 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
- 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
- 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30
我们可以看到col2长度变成了10,是10个59(Y的十六进制ASCII码是59),同时我们看到行地址由@0x1d3f变成了@0x2a7,说明这一行的空间容不下新增的数据,换了一个地方。(检查行目录也能发现这一点)同时,我们能看到lb(lock byte)由0x0变成了0x2,表明这条记录被该块事务槽列表中的第二个事务槽所标识的事务锁定。事务槽可以在块首部看到。
下面,看第5条数据在redo里面保存的是什么(怎么保证数据的重做),在文件里面搜索 tabn: 0 slot: 4(0x4) flag: 0x2c lock: 2, 我们会找到这么一段描述
- CHANGE #6 TYP:0 CLS:1 AFN:1 DBA:0x004161c9 OBJ:77125 SCN:0x0000.002796b6 SEQ:2 OP:11.5 ENC:0 RBL:0
- KTB Redo
- op: 0x02 ver: 0x01
- compat bit: 4 (post-11) padding: 0
- op: C uba: 0x00c0055a.0123.27
- KDO Op code: URP row dependencies Disabled
- xtype: XA flags: 0x00000000 bdba: 0x004161c9 hdba: 0x004161c8
- itli: 2 ispac: 0 maxfr: 4863
- tabn: 0 slot: 4(0x4) flag: 0x2c lock: 2 ckix: 50
- ncol: 4 nnew: 1 size: 4
- col 2: [10] 59 59 59 59 59 59 59 59 59 59
这描述的是一个改变世界,我们看第6行 op code:是URP(更新行片),第七行我们可以看到更新的块地址bdba和所在段的地址hdba。
第八行itli: 2 表明执行更新操作的事务正在使用第二个事务槽,跟数据块里面看到的一致。
第九行tabn: 0 slot: 4 表明我们在更新第一张表(一个块可能存储多个表的数据)的第5条记录。
最后两行,我们可以看出这条记录有4列(nclo:4),修改了一列(nnew:1),长度增加了4(size:4).并将第3列的值改成了YYYYYYYYYY。(保存了修改后的数据,方便重做)
接下来,看第5条数据在undo里面怎么保存的(怎么保证数据的回退),在文件里面搜索tabn: 0 slot: 4(0x4) flag: 0x2c,我们会找到如下一段描述:
- *-----------------------------
- * Rec #0x27 slt: 0x04 objn: 77125(0x00012d45) objd: 77125 tblspc: 0(0x00000000)
- * Layer: 11 (Row) opc: 1 rci 0x26
- Undo type: Regular undo Last buffer split: No
- Temp Object: No
- Tablespace Undo: No
- rdba: 0x00000000
- *-----------------------------
- KDO undo record:
- KTB Redo
- op: 0x02 ver: 0x01
- compat bit: 4 (post-11) padding: 0
- op: C uba: 0x00c0055a.0123.25
- KDO Op code: URP Disabled row dependencies
- xtype: XA flags: 0x00000000 bdba: 0x004161c9 hdba: 0x004161c8
- itli: 2 ispac: 0 maxfr: 4863
- tabn: 0 slot: 4(0x4) flag: 0x2c lock: 0 ckix: 50
- ncol: 4 nnew: 1 size: -4
- col 2: [ 6] 78 78 78 78 78 78
主要关注下面的六行数据,其实跟前面redo里面的数据差不多,就是size=-4,col2变成了6个78(x的十六进制ASCII码是78)。(保证数据能够回去以前的版本)
最后,我们可以在转储的redo里面寻找undo块改变的描述,文件里面搜索tabn: 0 slot: 4(0x4) flag: 0x2c lock: 0,我们会找到如下一段描述:
- CHANGE #11 TYP:0 CLS:36 AFN:3 DBA:0x00c0055a OBJ:4294967295 SCN:0x0000.002796b6 SEQ:4 OP:5.1 ENC:0 RBL:0
- ktudb redo: siz: 92 spc: 4078 flg: 0x0022 seq: 0x0123 rec: 0x27
- xid: 0x000a.004.00000467
- ktubu redo: slt: 4 rci: 38 opc: 11.1 objn: 77125 objd: 77125 tsn: 0
- Undo type: Regular undo Undo type: Last buffer split: No
- Tablespace Undo: No
- 0x00000000
- KDO undo record:
- KTB Redo
- op: 0x02 ver: 0x01
- compat bit: 4 (post-11) padding: 0
- op: C uba: 0x00c0055a.0123.25
- KDO Op code: URP row dependencies Disabled
- xtype: XA flags: 0x00000000 bdba: 0x004161c9 hdba: 0x004161c8
- itli: 2 ispac: 0 maxfr: 4863
- tabn: 0 slot: 4(0x4) flag: 0x2c lock: 0 ckix: 50
- ncol: 4 nnew: 1 size: -4
- col 2: [ 6] 78 78 78 78 78 78
第五行,代表这是一个undo块改变的描述,我们可以看到倒数几行跟undo里面的数据非常相似,因为这里记录的就是undo块的改变。
自此,我们基本上可以看清楚oracle是怎么描述数据的改变,然后才去真正去改变数据的。
相关推荐
Oracle 中的 redo 和 undo 机制是数据库的核心组件,它们一起协作以确保数据库的事务一致性和可靠性。在本文中,我们将深入探讨 redo 和 undo 机制的工作原理和协作机制。 Undo Mechanism Undo 机制是 Oracle 中的...
src =" node_modules/undo-redo-vuex/dist/undo-redo-vuex.min.js " > </ script > 模块 import undoRedo from "undo-redo-vuex" ; 用法 作为的标准, undo-redo-vuex可以用于以下设置: 如何在商店模块中...
Oracle_Undo与Redo的通俗 Oracle中的Undo和Redo是两个非常重要的概念,它们都是Oracle数据库中事务管理和恢复机制的关键组件。Undo和Redo都是为了确保数据库的一致性和可靠性。 什么是REDO? REDO记录transaction...
在Oracle中,日志文件发挥着关键作用,其中Undo日志和Redo日志是最核心的两种日志类型。 Undo日志,也称为撤销日志,主要用于事务处理中。它的主要作用是记录事务执行之前的数据库状态,即数据块在被修改之前的所有...
Oracle数据库是全球广泛使用的大型关系型数据库管理系统之一,其核心技术涉及数据存储、查询优化、并发控制、事务处理等多个方面。这篇读书笔记主要涵盖了Oracle数据库的一些核心概念和关键特性。 一、Oracle数据...
Oracle redo undo 是 Oracle 数据库中的一种机制,用于确保事务的可恢复性和撤销性。下面是对 redo undo 机制的详细解释。 redo 机制 redo 机制是 Oracle 数据库中的一种机制,用于记录事务的所有操作,以便在...
"redo与undo"是数据库管理中的关键概念,尤其在事务处理和数据恢复中扮演着核心角色。本文将深入探讨redo与undo机制,以及它们在数据库操作中的应用。 redo,全称重做日志(Redo Log),是数据库系统用来记录事务对...
在Oracle数据库管理中,调整redo日志文件大小是一项重要的维护工作,这直接影响到数据库的性能与稳定性。本文将深入探讨如何在Oracle环境中修改redo日志文件的大小,以及这一操作的具体步骤和注意事项。 ### 一、...
### Oracle的Temp表空间与UNDO表空间处理详解 #### 一、Temp表空间处理 在Oracle数据库中,Temp表空间主要用于存储临时对象,如排序、分组等操作产生的临时数据。这些操作可能会导致Temp表空间迅速增大。然而,...
安装npm install @intactile/redux-undo-redo 或者yarn add @intactile/redux-undo-redo配置此程序包配置了一组还原操作: import counter , { increment , decrement , setValue , INCREMENT , DECREMENT , SET_...
在Oracle数据库的Real Application Clusters (RAC)环境中,REDO和UNDO管理扮演着至关重要的角色,确保数据的安全性、事务处理以及数据恢复。在RAC架构中,多个实例共享同一物理数据库,这就需要对REDO和UNDO管理进行...
undo通过动态调度提供简单的undo-redo功能。 它是命令模式的一种实现,其中所有修改都已完成。b undo提供具有动态调度功能的简单undo-redo功能。 它是命令模式的一种实现,其中所有修改都是通过创建应用修改的命令...
这篇实验主要探讨了如何在Windows Forms(WinForm)应用中实现一个简单的Undo和Redo系统,同时支持设置最大撤销次数。 首先,我们需要理解撤销和重做是如何工作的。撤销操作通常是通过记录用户进行的每一项改变来...
本文将深入探讨“C# undo/redo框架”的核心概念、设计模式以及如何实现。 1. **命令模式**: 撤销/重做框架通常基于命令模式设计。命令模式是一种行为设计模式,将请求封装为一个对象,使发出请求与执行请求解耦。...
在讨论具体实现之前,我们先简要回顾一下Undo/Redo的基本概念: - **Undo**:撤销最近的一次操作,恢复到上一个状态。 - **Redo**:重做已经被撤销的操作。 为了支持多步Undo/Redo,我们需要记录每一次操作的状态...
Oracle数据库的Undo机制是其核心特性之一,它主要用于支持事务的回滚、读取一致性和闪回查询。本文将深入探讨Undo的详细功能及其优势。 首先,数据操纵语言(DML)包括INSERT、UPDATE、DELETE和MERGE等SQL语句,...
该原理是基于 Oracle 数据库的基本概念和机制,包括表、数据查询语句、DML 语句和 DDL 语句等。 在讨论 Oracle 在线备份之前,需要了解一些基本概念,包括表、数据查询语句和 DML 语句。表是数据库中最基本也是最...
本文深入分析了GIS软件中Undo与Redo操作的设计与实现,其主要内容包括Undo与Redo操作的原理、机制、流程以及实现方法。 1. Undo与Redo操作的原理: Undo与Redo操作是基于命令模式实现的。在这种模式下,每次用户...
总之,"undo: 低级的undo-redo功能"可能是一个专注于提供核心撤销/重做功能的库,它利用数据结构和设计模式来实现高效的历史记录管理,以支持用户在各种应用程序中的操作撤销和重做。在Rust中实现这样的功能需要对...