HelloDBA [English]
搜索Internet 搜索 HelloDBABA
  Oracle技术站。email: fuyuncat@gmail.com  MSN: fuyuncat@hotmail.com   acoug  acoug 

Oracle 内存回滚 (In Memory Undo)

[English]

作者: fuyuncat

来源: www.HelloDBA.com

日期: 2009-11-17 03:12:09

分享到  新浪微博 腾讯微博 人人网 i贴吧 开心网 豆瓣 淘宝 推特 Facebook GMail Blogger Orkut Google Bookmarks

    IMU是10g引入的一项新技术,并且是Oracle的专利技术。但是,在10g中似乎没有完全激活,以下的测试在10.2.0.3中无法通过,在11g中可以进行。
 

    在传统的事务更新过程中,如果一条数据记录被更新,就会从buffer cache中读取/分配一块UNDO数据块,并且立即会写入一条UNDO条目。如果同一个事务中有多条记录被更新,则undo buffer数据块中就会写入多条undo条目。引入IMU后,会从shared pool中分配出一个新的内存池——IMU pool。当一条数据记录被更新,仍然会从buffer cache中读取/分配一块undo数据块,但是,这块undo块并不会立即被更新,而是会在IMU pool中产生一个IMU node,IMU节点通过IMU map与数据记录更新对应。如果事务中有多条记录被修改,则IMU pool中就生产多个IMU nodes,而buffer中的undo block不会发生任何变化。当发生IMU commit或IMU flush时,才会通过IMU map将这些IMU node记录的undo信息写入undo buffer block中。并且,所有这些redo信息会和commit vector一起作为一个Redo条目写入Redo log中。整个过程中UNDO所产生的redo信息则大大减少。
 

    隐含参数_in_memory_undo用于控制IMU特性的开关,可以在会话/系统级立即生效,默认为true。另外一个隐含参数_IMU_pools则控制IMU pool的数量,默认为3。此外,目前IMU的使用还存在一些限制,如undo管理方式(undo_management)必须为auto,在RAC中无效,
 

SQL代码
  1. --建立测试表、数据   
  2. HELLODBA.COM>create table ttt (a number, b varchar2(20));   
  3.   
  4. Table created.   
  5.   
  6. HELLODBA.COM>begin  
  7.   2    for i in 1..2000 loop   
  8.   3      insert into ttt values (i, ''||i);   
  9.   4    end loop;   
  10.   5    commit;   
  11.   6  end;   
  12.   7  /   
  13.   
  14. PL/SQL procedure successfully completed.   
  15.   
  16. HELLODBA.COM>select a   
  17.   2  from (select a, dbms_rowid.rowid_block_number(ROWID) block_id, lag(dbms_rowid.rowid_block_number(ROWID)) over (order by rowid) as pre_block_id from ttt)   
  18.   3  where block_id != pre_block_id;   
  19.   
  20.          A   
  21. ----------   
  22.       1124   
  23.       1643   
  24.          1   

IMU Commit

    让我们看下IMU commit与传统事务commit时产生的redo size的变化。首先看传统模式下,
 

SQL代码
  1. HELLODBA.COM>conn demo/demo@ora11   
  2. Connected.   
  3. HELLODBA.COM>alter session set "_in_memory_undo"=false;   
  4.   
  5. Session altered.   
  6.   
  7. HELLODBA.COM>update ttt set b='X' where a=1124;   
  8.   
  9. 1 row updated.   
  10.   
  11. HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# and b.name in ('redo entries''redo size''IMU commits');   
  12.   
  13. NAME                                                                  VALUE   
  14. ---------------------------------------------------------------- ----------   
  15. redo entries                                                              4   
  16. redo size                                                              1600   
  17. IMU commits                                                               0   
  18.   
  19. HELLODBA.COM>update ttt set b='Y' where a=1643;   
  20.   
  21. 1 row updated.   
  22.   
  23. HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# and b.name in ('redo entries''redo size''IMU commits');   
  24.   
  25. NAME                                                                  VALUE   
  26. ---------------------------------------------------------------- ----------   
  27. redo entries                                                              5   
  28. redo size                                                              1960   
  29. IMU commits                                                               0   
  30.   
  31. HELLODBA.COM>update ttt set b='Z' where a=1;   
  32.   
  33. 1 row updated.   
  34.   
  35. HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# and b.name in ('redo entries''redo size''IMU commits');   
  36.   
  37. NAME                                                                  VALUE   
  38. ---------------------------------------------------------------- ----------   
  39. redo entries                                                              6   
  40. redo size                                                              2320   
  41. IMU commits                                                               0   
  42.   
  43. HELLODBA.COM>commit;   
  44.   
  45. Commit complete.   
  46.   
  47. HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# and b.name in ('redo entries''redo size''IMU commits');   
  48.   
  49. NAME                                                                  VALUE   
  50. ---------------------------------------------------------------- ----------   
  51. redo entries                                                              7   
  52. redo size                                                              2416   
  53. IMU commits                                                               0   

    可以看到,每一条数据被update都产生一条redo 条目。
 

    然后,我们激活IMU,再重复上述事务过程,
 

SQL代码
  1. HELLODBA.COM>conn demo/demo@ora11   
  2. Connected.   
  3. HELLODBA.COM>alter session set "_in_memory_undo"=true;   
  4.   
  5. Session altered.   
  6.   
  7. HELLODBA.COM>update ttt set b='X' where a=1124;   
  8.   
  9. 1 row updated.   
  10.   
  11. HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# an   
  12. d b.name in ('redo entries''redo size''IMU commits');   
  13.   
  14. NAME                                                                  VALUE   
  15. ---------------------------------------------------------------- ----------   
  16. redo entries                                                              3   
  17. redo size                                                              1084   
  18. IMU commits                                                               0   
  19.   
  20. HELLODBA.COM>update ttt set b='Y' where a=1643;   
  21.   
  22. 1 row updated.   
  23.   
  24. HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# an   
  25. d b.name in ('redo entries''redo size''IMU commits');   
  26.   
  27. NAME                                                                  VALUE   
  28. ---------------------------------------------------------------- ----------   
  29. redo entries                                                              3   
  30. redo size                                                              1084   
  31. IMU commits                                                               0   
  32.   
  33. HELLODBA.COM>update ttt set b='Z' where a=1;   
  34.   
  35. 1 row updated.   
  36.   
  37. HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# an   
  38. d b.name in ('redo entries''redo size''IMU commits');   
  39.   
  40. NAME                                                                  VALUE   
  41. ---------------------------------------------------------------- ----------   
  42. redo entries                                                              3   
  43. redo size                                                              1084   
  44. IMU commits                                                               0   
  45.   
  46. HELLODBA.COM>commit;   
  47.   
  48. Commit complete.   
  49.   
  50. HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# an   
  51. d b.name in ('redo entries''redo size''IMU commits');   
  52.   
  53. NAME                                                                  VALUE   
  54. ---------------------------------------------------------------- ----------   
  55. redo entries                                                              4   
  56. redo size                                                              2176   
  57. IMU commits                                                               1   

    可见redo数量并没有随着数据的更新而增加,而是在IMU commit时增加。而当1条DML语句更新多条记录时,也可以使用到IMU:
 

SQL代码
  1. HELLODBA.COM>conn demo/demo@ora11   
  2. Connected.   
  3. HELLODBA.COM>alter session set "_in_memory_undo"=true;   
  4.   
  5. Session altered.   
  6.   
  7. HELLODBA.COM>update ttt set b='X' where a in (1643, 1124, 1);   
  8.   
  9. rows updated.   
  10.   
  11. HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# an   
  12. d b.name in ('redo entries''redo size''IMU commits');   
  13.   
  14. NAME                                                                  VALUE   
  15. ---------------------------------------------------------------- ----------   
  16. redo entries                                                              3   
  17. redo size                                                              1084   
  18. IMU commits                                                               0   
  19.   
  20. HELLODBA.COM>commit;   
  21.   
  22. Commit complete.   
  23.   
  24. HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# an   
  25. d b.name in ('redo entries''redo size''IMU commits');   
  26.   
  27. NAME                                                                  VALUE   
  28. ---------------------------------------------------------------- ----------   
  29. redo entries                                                              4   
  30. redo size                                                              2344   
  31. IMU commits                                                               1   

    从上面的例子中你也许注意到了,尽管UPDATE过程中redo size没有变化,但是,在IMU commit时,redo size的变化却很大,比传统模式下的commit产生的redo大许多。这是因为在IMU commit中,不仅仅包含了commit vector,还包含了commit之前数据变化,并且这些redo数据的写入是一次批量写入。我们可以将这个redo条目dump出来观察其内容:
 

SQL代码
  1. HELLODBA.COM>conn demo/demo@ora11   
  2. Connected.   
  3. HELLODBA.COM>set serveroutput on  
  4. HELLODBA.COM>var v_bt number;   
  5. HELLODBA.COM>var v_et number;   
  6. HELLODBA.COM>alter session set "_in_memory_undo"=false;   
  7.   
  8. Session altered.   
  9.   
  10. HELLODBA.COM>update tt set x=1 where rownum <= 1;   
  11.   
  12. 1 row updated.   
  13.   
  14. HELLODBA.COM>update tt set x=2 where rownum <= 1;   
  15.   
  16. 1 row updated.   
  17.   
  18. HELLODBA.COM>update tt set x=3 where rownum <= 1;   
  19.   
  20. 1 row updated.   
  21.   
  22. HELLODBA.COM>begin  
  23.   2   select current_scn into :v_bt from v$database;   
  24.   3   dbms_output.put_line(''||:v_bt);   
  25.   4  end;   
  26.   5  /   
  27. 6328064   
  28.   
  29. PL/SQL procedure successfully completed.   
  30.   
  31. HELLODBA.COM>commit;   
  32.   
  33. Commit complete.   
  34.   
  35. HELLODBA.COM>begin  
  36.   2   select current_scn into :v_et from v$database;   
  37.   3   dbms_output.put_line(''||:v_et);   
  38.   4  end;   
  39.   5  /   
  40. 6328067   
  41.   
  42. PL/SQL procedure successfully completed.   
  43.   
  44. HELLODBA.COM>declare  
  45.   2   v_log varchar2(2000);   
  46.   3   v_sql varchar2(4000);   
  47.   4  begin  
  48.   5   select a.member into v_log from v$logfile a, v$log b where a.group#=b.groupand b.status='CUR  
  49. RENT' and rownum <= 1;   
  50.   6   execute immediate 'alter system switch logfile';   
  51.   7   v_sql := 'alter system dump logfile '''||v_log||''' SCN MIN '||:v_bt||' SCN MAX '||:v_et;   
  52.   8   execute immediate v_sql;   
  53.   9  end;   
  54.  10  /   
  55.   
  56. PL/SQL procedure successfully completed.   

    可以看到,在trace日志中,这一redo条目包含了多个change:
 

SQL代码
  1. REDO RECORD - Thread:1 RBA: 0x0000c8.00000f39.0010 LEN: 0x046c VLD: 0x0d   
  2. SCN: 0x0000.00608ed4 SUBSCN:  1 11/16/2009 14:59:10   
  3. CHANGE #1 TYP:2 CLS: 1 AFN:4 DBA:0x010016cf OBJ:74952 SCN:0x0000.00602dc7 SEQ:  4 OP:11.19   
  4. KTB Redo    
  5. ...   
  6. CHANGE #2 TYP:0 CLS:17 AFN:3 DBA:0x00c00009 OBJ:4294967295 SCN:0x0000.00608e9b SEQ:  2 OP:5.2   
  7. ...   
  8. CHANGE #8 TYP:0 CLS:18 AFN:3 DBA:0x00c006f7 OBJ:4294967295 SCN:0x0000.00608ed4 SEQ:  2 OP:5.1   
  9. ...   

IMU Flush

    IMU pool也是按照LRU算法管理的。当IMU pool没有足够空闲内存可分配时,会将buffer链上LRU段的buffer块flush出来。其他一些事件也会导致IMU flush的发生,如switch logfile、rollback。但是,尽管IMU pool是从shared pool中分配的,手动flush shared pool并不会导致IMU flush。当IMU flush发生时,也会将undo、redo数据批量写入。
 

SQL代码
  1. HELLODBA.COM>conn demo/demo@ora11   
  2. Connected.   
  3. HELLODBA.COM>alter session set "_in_memory_undo"=true;   
  4.   
  5. Session altered.   
  6.   
  7. HELLODBA.COM>update tt set x=1;   
  8.   
  9. 1 row updated.   
  10.   
  11. HELLODBA.COM>update tt set x=2;   
  12.   
  13. 1 row updated.   
  14.   
  15. HELLODBA.COM>update tt set x=3;   
  16.   
  17. 1 row updated.   
  18.   
  19. HELLODBA.COM>select b.name, a.value from v$sysstat a, v$statname b where a.statistic#=b.statistic# and b.name like '%IMU%';   
  20.   
  21. NAME                                                                  VALUE   
  22. ---------------------------------------------------------------- ----------   
  23. IMU commits                                                             320   
  24. IMU Flushes                                                             159   
  25. IMU contention                                                           19   
  26. ...   
  27.   
  28. 13 rows selected.   
  29.   
  30. HELLODBA.COM>alter system switch logfile;   
  31.   
  32. System altered.   
  33.   
  34. HELLODBA.COM>select b.name, a.value from v$sysstat a, v$statname b where a.statistic#=b.statistic# and b.name like '%IMU%';   
  35.   
  36. NAME                                                                  VALUE   
  37. ---------------------------------------------------------------- ----------   
  38. IMU commits                                                             320   
  39. IMU Flushes                                                             160   
  40. IMU contention                                                           20   
  41. ...   
  42.   
  43. 13 rows selected.   

    Tips: 通过dump出事务的undo block,可以比较IMU commit/flush前后undo block的变化——commit/flush之前没有写入数据。

IMU CR

    在传统事务中,需要进行一致性读时,会从相应的UNDO数据块中读入undo数据进行undo操作。而在IMU中,在发生IMU commit或IMU flush之前,这些undo数据并未写入UNDO数据块中,此时一致性读就从IMU pool中读取相应的IMU node中的undo信息。
 

SQL代码
  1. --Session 1:   
  2. HELLODBA.COM>conn demo/demo@ora11   
  3. Connected.   
  4. HELLODBA.COM>alter session set "_in_memory_undo"=true;   
  5.   
  6. Session altered.   
  7.   
  8. HELLODBA.COM>update tt set x=1;   
  9.   
  10. 1 row updated.   
  11.   
  12. HELLODBA.COM>update tt set x=2;   
  13.   
  14. 1 row updated.   
  15.   
  16. HELLODBA.COM>update tt set x=3;   
  17.   
  18. 1 row updated.   
  19.   
  20. --Session 2:   
  21. HELLODBA.COM>conn demo/demo@ora11   
  22. Connected.   
  23. HELLODBA.COM>alter system flush buffer_cache;   
  24.   
  25. System altered.   
  26.   
  27. HELLODBA.COM>alter session set tracefile_identifier=IMU_CR;   
  28.   
  29. Session altered.   
  30.   
  31. HELLODBA.COM>alter session set events '10046 trace name context forever, level 8';   
  32.   
  33. Session altered.   
  34.   
  35. HELLODBA.COM>select * from tt;   
  36.   
  37.          X   
  38. ----------   
  39.          3   
  40.   
  41. HELLODBA.COM>alter session set events '10046 trace name context off';   
  42.   
  43. Session altered.   
  44.   
  45. HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# and b.name like '%IMU%';   
  46.   
  47. NAME                                                                  VALUE   
  48. ---------------------------------------------------------------- ----------   
  49. ...   
  50. IMU CR rollbacks                                                          3   
  51. ...   
  52.   
  53. 13 rows selected.   

    从trace文件可以看到,我们将buffer cache的内容都flush了,但是并未从undo文件中读取undo 信息(没有相应的IO等待)。
 

    --- Fuyuncat Mark ---

Top

Copyright ©2005,HelloDBA.Com 保留一切权利

申明
by fuyuncat