设为首页 收藏本站
查看: 611|回复: 0

[经验分享] Oracle基本数据改变原理浅析(redo与undo)--oracle核心技术读书笔记一

[复制链接]

尚未签到

发表于 2016-8-6 15:36:10 | 显示全部楼层 |阅读模式
  在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. 准备工作(主要清除回收站删除信息,设置块读取代价,估值计算依据等)
[sql] view plaincopy DSC0000.jpg DSC0001.jpg  



  • 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. 创建表与索引
[html] view plaincopy 



  • 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. 统计表信息
[sql] view plaincopy 



  • begin  
  •     dbms_stats.gather_table_stats(  
  •         ownname      => user,  
  •         tabname      =>'T1',  
  •         method_opt   => 'for all columns size 1'  
  •     );  
  • end;  
  • /  

         6.查看表占用的块情况,和每一个块有多少条数据
[sql] view plaincopy 



  • 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. 转储数据块
[sql] view plaincopy 



  • alter system switch logfile;  
  •   
  • execute dump_seg('t1')  

          8. 做更新
[sql] view plaincopy 



  • update  
  •     /*+ index(t1 t1_i1) */  
  •     t1  
  • set  
  •     v1 = 'YYYYYYYYYY'  
  • where  
  •     id between 5 and 9  
  • ;  

           9.  转储更新块之后的数据块和undo块(发生检查点语句执行后,下一个语句等5,6s再执行,发生检查点只是告诉oracle将脏数据写入磁盘,需要一点时间)
 
 
[sql] view plaincopy 



  • pause Query the IMU structures now  (@core_imu_01.sql)  

[sql] view plaincopy 



  • alter system checkpoint;--发生检查点,让数据写到磁盘  

[sql] view plaincopy 



  • execute dump_seg('t1')  
  • execute dump_undo_block  

            10. 转储redo块
[sql] view plaincopy 



  • rollback;  
  • commit;  
  •   
  • execute dump_log  

             11. 定位转储信息文件位置  
[sql] view plaincopy 



  • 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之前:
[sql] view plaincopy 



  • 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之后:
[sql] view plaincopy 



  • 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, 我们会找到这么一段描述
[sql] view plaincopy 



  • 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,我们会找到如下一段描述:
[sql] view plaincopy 



  • *-----------------------------  
  • * 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,我们会找到如下一段描述:
 
[sql] view plaincopy 



  • 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是怎么描述数据的改变,然后才去真正去改变数据的。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-253757-1-1.html 上篇帖子: 【Oracle SQL】Oracle SQL 之七 SQL面试题比武招亲 下篇帖子: oracle通过透明网关,创建dblink,访问ms sql server和其他数据库
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表