关注Oracle10g中开始出现的IMU(In Memory Undo)

IMU是10g引入的一项新技术,并且是Oracle的专利技术。但是,在10g中似乎没有完全激活,以下的测试在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代码
--建立测试表、数据   
HELLODBA.COM>create table ttt (a number, b varchar2(20));   
  
Table created.   
  
HELLODBA.COM>begin  
  2    for i in 2000 loop   
  3      insert into ttt values (i, ''||i);   
  4    end loop;   
  5    commit;   
  6  end;   
  7  /   
  
PL/SQL procedure successfully completed.   
  
HELLODBA.COM>select a   
  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)   
  3  where block_id != pre_block_id;   
  
         A   
----------   
      1124   
      1643   
         1   
IMU Commit
    让我们看下IMU commit与传统事务commit时产生的redo size的变化。首先看传统模式下,

SQL代码
HELLODBA.COM>conn demo/demo@ora11   
Connected.   
HELLODBA.COM>alter session set "_in_memory_undo"=false;   
  
Session altered.   
  
HELLODBA.COM>update ttt set b='X' where a=1124;   
  
1 row updated.   
  
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');   
  
NAME                                                                  VALUE   
---------------------------------------------------------------- ----------   
redo entries                                                              4   
redo size                                                              1600   
IMU commits                                                               0   
  
HELLODBA.COM>update ttt set b='Y' where a=1643;   
  
1 row updated.   
  
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');   
  
NAME                                                                  VALUE   
---------------------------------------------------------------- ----------   
redo entries                                                              5   
redo size                                                              1960   
IMU commits                                                               0   
  
HELLODBA.COM>update ttt set b='Z' where a=1;   
  
1 row updated.   
  
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');   
  
NAME                                                                  VALUE   
---------------------------------------------------------------- ----------   
redo entries                                                              6   
redo size                                                              2320   
IMU commits                                                               0   
  
HELLODBA.COM>commit;   
  
Commit complete.   
  
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');   
  
NAME                                                                  VALUE   
---------------------------------------------------------------- ----------   
redo entries                                                              7   
redo size                                                              2416   
IMU commits                                                               0   
    可以看到,每一条数据被update都产生一条redo 条目。

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

SQL代码
HELLODBA.COM>conn demo/demo@ora11   
Connected.   
HELLODBA.COM>alter session set "_in_memory_undo"=true;   
  
Session altered.   
  
HELLODBA.COM>update ttt set b='X' where a=1124;   
  
1 row updated.   
  
HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# an   
d b.name in ('redo entries', 'redo size', 'IMU commits');   
  
NAME                                                                  VALUE   
---------------------------------------------------------------- ----------   
redo entries                                                              3   
redo size                                                              1084   
IMU commits                                                               0   
  
HELLODBA.COM>update ttt set b='Y' where a=1643;   
  
1 row updated.   
  
HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# an   
d b.name in ('redo entries', 'redo size', 'IMU commits');   
  
NAME                                                                  VALUE   
---------------------------------------------------------------- ----------   
redo entries                                                              3   
redo size                                                              1084   
IMU commits                                                               0   
  
HELLODBA.COM>update ttt set b='Z' where a=1;   
  
1 row updated.   
  
HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# an   
d b.name in ('redo entries', 'redo size', 'IMU commits');   
  
NAME                                                                  VALUE   
---------------------------------------------------------------- ----------   
redo entries                                                              3   
redo size                                                              1084   
IMU commits                                                               0   
  
HELLODBA.COM>commit;   
  
Commit complete.   
  
HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# an   
d b.name in ('redo entries', 'redo size', 'IMU commits');   
  
NAME                                                                  VALUE   
---------------------------------------------------------------- ----------   
redo entries                                                              4   
redo size                                                              2176   
IMU commits                                                               1   
    可见redo数量并没有随着数据的更新而增加,而是在IMU commit时增加。而当1条DML语句更新多条记录时,也可以使用到IMU:

SQL代码
HELLODBA.COM>conn demo/demo@ora11   
Connected.   
HELLODBA.COM>alter session set "_in_memory_undo"=true;   
  
Session altered.   
  
HELLODBA.COM>update ttt set b='X' where a in (1643, 1124, 1);   
  
3 rows updated.   
  
HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# an   
d b.name in ('redo entries', 'redo size', 'IMU commits');   
  
NAME                                                                  VALUE   
---------------------------------------------------------------- ----------   
redo entries                                                              3   
redo size                                                              1084   
IMU commits                                                               0   
  
HELLODBA.COM>commit;   
  
Commit complete.   
  
HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# an   
d b.name in ('redo entries', 'redo size', 'IMU commits');   
  
NAME                                                                  VALUE   
---------------------------------------------------------------- ----------   
redo entries                                                              4   
redo size                                                              2344   
IMU commits                                                               1   
    从上面的例子中你也许注意到了,尽管UPDATE过程中redo size没有变化,但是,在IMU commit时,redo size的变化却很大,比传统模式下的commit产生的redo大许多。这是因为在IMU commit中,不仅仅包含了commit vector,还包含了commit之前数据变化,并且这些redo数据的写入是一次批量写入。我们可以将这个redo条目dump出来观察其内容:

SQL代码
HELLODBA.COM>conn demo/demo@ora11   
Connected.   
HELLODBA.COM>set serveroutput on  
HELLODBA.COM>var v_bt number;   
HELLODBA.COM>var v_et number;   
HELLODBA.COM>alter session set "_in_memory_undo"=false;   
  
Session altered.   
  
HELLODBA.COM>update tt set x=1 where rownum <= 1;   
  
1 row updated.   
  
HELLODBA.COM>update tt set x=2 where rownum <= 1;   
  
1 row updated.   
  
HELLODBA.COM>update tt set x=3 where rownum <= 1;   
  
1 row updated.   
  
HELLODBA.COM>begin  
  2   select current_scn into :v_bt from v$database;   
  3   dbms_output.put_line(''||:v_bt);   
  4  end;   
  5  /   
6328064   
  
PL/SQL procedure successfully completed.   
  
HELLODBA.COM>commit;   
  
Commit complete.   
  
HELLODBA.COM>begin  
  2   select current_scn into :v_et from v$database;   
  3   dbms_output.put_line(''||:v_et);   
  4  end;   
  5  /   
6328067   
  
PL/SQL procedure successfully completed.   
  
HELLODBA.COM>declare  
  2   v_log varchar2(2000);   
  3   v_sql varchar2(4000);   
  4  begin  
  5   select a.member into v_log from v$logfile a, v$log b where a.group#=b.group# and b.status='CUR  
RENT' and rownum <= 1;   
  6   execute immediate 'alter system switch logfile';   
  7   v_sql := 'alter system dump logfile '''||v_log||''' SCN MIN '||:v_bt||' SCN MAX '||:v_et;   
  8   execute immediate v_sql;   
  9  end;   
10  /   
  
PL/SQL procedure successfully completed.   
    可以看到,在trace日志中,这一redo条目包含了多个change:

SQL代码
REDO RECORD - Thread:1 RBA: 0x0000c8.00000f0010 LEN: 0x046c VLD: 0x0d   
SCN: 0x0000608ed4 SUBSCN:  1 11/16/2009 14:59:10   
CHANGE #1 TYP:2 CLS: 1 AFN:4 DBA:0x010016cf OBJ:74952 SCN:0x0000602dc7 SEQ:  4 OP:19   
KTB Redo   
...   
CHANGE #2 TYP:0 CLS:17 AFN:3 DBA:0x00c00009 OBJ:4294967295 SCN:0x0000608e9b SEQ:  2 OP:5.2   
...   
CHANGE #8 TYP:0 CLS:18 AFN:3 DBA:0x00c006f7 OBJ:4294967295 SCN:0x0000608ed4 SEQ:  2 OP:5.1   
...   
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代码
HELLODBA.COM>conn demo/demo@ora11   
Connected.   
HELLODBA.COM>alter session set "_in_memory_undo"=true;   
  
Session altered.   
  
HELLODBA.COM>update tt set x=1;   
  
1 row updated.   
  
HELLODBA.COM>update tt set x=2;   
  
1 row updated.   
  
HELLODBA.COM>update tt set x=3;   
  
1 row updated.   
  
HELLODBA.COM>select b.name, a.value from v$sysstat a, v$statname b where a.statistic#=b.statistic# and b.name like '%IMU%';   
  
NAME                                                                  VALUE   
---------------------------------------------------------------- ----------   
IMU commits                                                             320   
IMU Flushes                                                             159   
IMU contention                                                           19   
...   
  
13 rows selected.   
  
HELLODBA.COM>alter system switch logfile;   
  
System altered.   
  
HELLODBA.COM>select b.name, a.value from v$sysstat a, v$statname b where a.statistic#=b.statistic# and b.name like '%IMU%';   
  
NAME                                                                  VALUE   
---------------------------------------------------------------- ----------   
IMU commits                                                             320   
IMU Flushes                                                             160   
IMU contention                                                           20   
   
  
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代码
--Session 1:   
HELLODBA.COM>conn demo/demo@ora11   
Connected.   
HELLODBA.COM>alter session set "_in_memory_undo"=true;   
  
Session altered.   
  
HELLODBA.COM>update tt set x=1;   
  
1 row updated.   
  
HELLODBA.COM>update tt set x=2;   
  
1 row updated.   
  
HELLODBA.COM>update tt set x=3;   
  
1 row updated.   
  
--Session 2:   
HELLODBA.COM>conn demo/demo@ora11   
Connected.   
HELLODBA.COM>alter system flush buffer_cache;   
  
System altered.   
  
HELLODBA.COM>alter session set tracefile_identifier=IMU_CR;   
  
Session altered.   
  
HELLODBA.COM>alter session set events '10046 trace name context forever, level 8';   
  
Session altered.   
  
HELLODBA.COM>select * from tt;   
  
         X   
----------   
         3   
  
HELLODBA.COM>alter session set events '10046 trace name context off';   
  
Session altered.   
  
HELLODBA.COM>select b.name, a.value from v$mystat a, v$statname b where a.statistic#=b.statistic# and b.name like '%IMU%';   
  
NAME                                                                  VALUE   
---------------------------------------------------------------- ----------   
...   
IMU CR rollbacks                                                          3   
   
  
13 rows selected.   
    从trace文件可以看到,我们将buffer cache的内容都flush了,但是并未从undo文件中读取undo 信息(没有相应的IO等待)。
标签: 暂无标签
oraunix

写了 199 篇文章,拥有财富 1026,被 339 人关注

转播转播 分享分享 分享淘帖
回复

使用道具

P6 | 发表于 2011-1-21 17:34:28
Oracle 10g 默认是使用 IMU 这个特性的。通过隐含参数 _in_memory_undo 可以关闭这个特性。因为是隐含参数,也侧面反映出该特性并非那么成熟。搜索一下 Metalink,有不少关于 IMU 的 Bug,而 UNOD 的 Bug 一旦遇到,不停 DB 恐怕都很难解决。所以,对于可用性要求比较高的系统,现在起用该特性还是需要三思。
回复

使用道具

P6 | 发表于 2011-1-21 17:36:11
我们知道在Oracle 10g之前,Oracle执行事务之前必须在undo表空间记录事务的执行情况,如分配事务表(TX Table),分配undo block。进行这些操作必须将undo block读至buffer cache,这又会涉及到Oracle latch分配和latch pin等操作。Oracle为了实现一致性读取要求,假如undo block没在buffer cache,又需从undo表空间物理读取block,这些资源密集型操作势必影响其性能。从Oracle 10g开始,Oracle开始推出一新特性memory undo,即专门在shared pool中开辟内存处理事务,以下就简单研究一下In memory UNDO,简称imu,注意在RAC情况下imu不被支持,搜索metalink目前imu存在较多bug

研究平台为

引用
SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Prod
PL/SQL Release 10.2.0.3.0 - Production
CORE    10.2.0.3.0      Production
TNS for Linux: Version 10.2.0.3.0 - Production
NLSRTL Version 10.2.0.3.0 - Production


Oralce 10g是否使用由隐含参数_in_memory_undo决定,默认情况为TRUE。

引用
_in_memory_undo                          TRUE                 Make in memory undo for top level transactions


此外imu还涉及以下参数
imu pool在shard_pool分配的个数,注意此参数和实际分配的个数不符,只作为阀值存在:

引用
_imu_pools                               3                    in memory undo pools

DBWR是否刷新imu pool:

引用
_db_writer_flush_imu                     TRUE                 If FALSE, DBWR will not downgrade IMU txns for AGING

递归操作是否启动imu:

引用
_recursive_imu_transactions              FALSE                recursive transactions may be IMU


基于以上参数我们再深入探索。
查看imu pool在shared_pool分配的大小,可以看到在shared_pool中分配了1235304 BYTES。

引用
SQL> select * from v$sgastat where name='KTI-UNDO';

POOL         NAME                                          BYTES
------------ ---------------------------------------- ----------
shared pool  KTI-UNDO                                    1235304

进一步通过X$KTIFP,可以查看每一个imu pool内存使用情况,可以看到目前系统中imu pool共为18个(为参数transactions*10%),每个pool大小为65535 bytes(对于32为系统为64k + 24bytes=65535 bytes,对于64位系统为128k + 1k=132096 bytes),可以看到pool大小为64k,所以imu只针对小事务使用。

引用
SQL> show parameter transactions

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
transactions                         integer     187
SQL> select KTIFPNO,KTIFPPSI from X$KTIFP;

   KTIFPNO   KTIFPPSI
---------- ----------
         0      65535
         1      65535
         2      65535
         3         16
         4      65535
         5      65535
         6      65535
         7      65535
         8      65535
         9      65535
        10      65535

   KTIFPNO   KTIFPPSI
---------- ----------
        11      65535
        12      65535
        13      65535
        14      65535
        15      65535
        16      65535
        17      65535

18 rows selected.


Oracle alert日志也可以看到分配给imu pool数量,用ILAT =18表示

引用
Autotune of undo retention is turned on.
IMODE=BR
ILAT =18
LICENSE_MAX_USERS = 0
SYS auditing is disabled


通过以下视图可以查看Oracle触发imu刷新内存的条件

引用
SQL> col INDX for 999
SQL> col KTIFFCAT for a50
SQL> select indx,ktiffcat,ktiffflc from X$KTIFF;
INDX KTIFFCAT                                             KTIFFFLC
---- -------------------------------------------------- ----------
   0 Undo pool overflow flushes                                  0
   1 Stack cv flushes                                        34142
   2 Multi-block undo flushes                                    0
   3 Max. chgs flushes                                       51607
   4 NTP flushes                                               492
   5 Contention flushes                                        679
   6 Redo pool overflow flushes                                  1
   7 Logfile space flushes                                       0
   8 Multiple persistent buffer flushes                          0
   9 Bind time flushes                                           0
  10 Rollback flushes                                         4945

INDX KTIFFCAT                                             KTIFFFLC
---- -------------------------------------------------- ----------
  11 Commit flushes                                         215244
  12 Recursive txn flushes                                   49992
  13 Redo only CR flushes                                        0
  14 Ditributed txn flushes                                      0
  15 Set txn use rbs flushes                                     0
  16 Bitmap state change flushes                             85557
  17 Presumed commit violation                                   0

18 rows selected.


当系统启用imu,部分事务即可通过imu提交。

引用
SQL> select name,value from v$sysstat where name like '%commits%';

NAME                                                                  VALUE
---------------------------------------------------------------- ----------
user commits                                                          42785
IMU commits                                                           40411


当然了Oracle使用imu特性也是通过latch保护的,每一个imu pool都有相应的latch保护。每个事务独立的使用imu pool。

引用
SQL> select LATCH#,NAME,GETS,MISSES from v$latch where name ='In memory undo latch';

    LATCH# NAME                                           GETS     MISSES
---------- ---------------------------------------- ---------- ----------
       192 In memory undo latch                       14548635         74

SQL> select NAME,GETS,MISSES from v$latch_children where name='In memory undo latch';

NAME                                           GETS     MISSES
---------------------------------------- ---------- ----------
In memory undo latch                        2997946         31
In memory undo latch                         286606          4
In memory undo latch                        6641899         13
In memory undo latch                        4328804         13
In memory undo latch                         113778         13
In memory undo latch                         121081          0
In memory undo latch                          58811          0
In memory undo latch                              0          0
In memory undo latch                              0          0
In memory undo latch                              0          0
In memory undo latch                              0          0

NAME                                           GETS     MISSES
---------------------------------------- ---------- ----------
In memory undo latch                              0          0
In memory undo latch                              0          0
In memory undo latch                              0          0
In memory undo latch                              0          0
In memory undo latch                              0          0
In memory undo latch                              0          0
In memory undo latch                              0          0

18 rows selected.


简单总结:
Oracle 10g通过在shared_pool中开辟一小块内存,用来缓存未提交的小事务相关的undo block,然后通过一定的触发条件,有序批量的写出,有效的避免了不必要的读写操作,对于性能的提高启到了一定的作用。


回复

使用道具

游客 | 发表于 2012-4-11 08:59:47
标记,未读
回复

使用道具

P6 | 发表于 2012-4-12 12:02:07
在我的视频里,讲解了IMU机制。
回复

使用道具

P4 | 发表于 2012-4-13 10:24:26
老师视频啥时候出啊,等不及了啊
回复

使用道具

P6 | 发表于 2012-4-13 10:53:07
处理中。。。
回复

使用道具

您需要登录后才可以回帖 登录 | 加入社区

本版积分规则

意见
反馈