Oracle 10gR2 对Commit的改进(新选择)


Oracle 10gR2中引人了一个新的参数commit_write, 可以通过调整此参数调整Oracle在Commit时的行为特征.

随着web 2.0网站的涌现, 写越来越成为传统的RDBMS数据库的瓶颈, 而lgwr的写一直是Oracle数据库中最终的瓶颈所在, 在早期的版本中, 所有的事务必须先申请redo log buffer的空间才能进行变更, 提交变更时也必须确保底层的IO写到物理磁盘, 除非使用磁盘或者存储的Write Cache,否则写密集的应用会遭遇大量的log file sync, 用户端在提交Commit命令之后必须等待lgwr进程写好此事务相关的redo日志才能返回, 以达到满足ACID标准的Durability的目的. 或许是为了吸引部分此类用户使用Oracle数据库来解决此类问题, Oracle改进了lgwr Commit相关的处理, 以弱化Durability的方式来帮助用户提高写的效率.

commit_write 参数的值可在下列四项中进行选择.

  • immediate , wait (10gR2之前的版本的内置选择, 10gR2以及以后的版本的默认选项)
  • immediate , nowait
  • batch , wait
  • batch , nowait
    默认的Commit行为 新增选项>
    Immediate 日志信息会立即写入磁盘
    (每次提交时都必须做一次磁盘I/O操作)
    batch Oracle会对日志信息进行缓冲.
    Oracle会按照其自己安排的时间将此日志信息写入到磁盘.
    也就是说,多个I/O操作将组成一个批次进行处理
    wait 直到提交(commit)操作成功完成,
    commit命令才会返回
    nowait Oracle不会等待提交返回,
    而是直接将控制权返回给客户端

    我在自己的台式机上对这个参数做了下简单的测试. 测试脚本如下

    view source print?
    01 --创建一个sequence, 并赋予一个很大的cache_size, 使得sequence 尽量不要成为此程序的瓶颈.
    02 create sequence seq_commit_test start with 100000 increment by 1 cache 50000;
    03   
    04 --一个简单的存储过程, 给定一个描述, 往表中插入10000条记录, 每条记录会提交一次.
    05 --此程序改写自Tim Hall的脚本, 参考http://www.oracle-base.com/articles/10g/Commit_10gR2.php
    06 create or replace procedure do_loop (p_type  in  varchar2) as
    07   l_start  NUMBER;
    08   l_loops  NUMBER := 10000;
    09 BEGIN
    10   l_start := DBMS_UTILITY.get_time;
    11   FOR i IN 1 .. l_loops LOOP
    12     INSERT INTO commit_test (id, description)
    13     VALUES (seq_commit_test.nextval, 'Description for ' || i);
    14     commit;
    15   END LOOP;
    16   DBMS_OUTPUT.put_line(RPAD('COMMIT WRITE ' || p_type, 30) || ': ' || (DBMS_UTILITY.get_time - l_start));
    17 END;
    18 /

    1. 测试单进程时,次参数的表现.
    同时启动两个session, 一个以sys用户登陆, 一个以用户session登陆(我的测试中为James用户).
    过程如下.

  • 在sys用户下, 修改系统级别的commit_write参数,
  • 查看v$sysstat 中以下几个参数的值.
  • view source print?
    1 sys@DBMAIN>@stat
    2 sys@DBMAIN>select * from v$sysstat where statistic# in (175,177,124,125);
    3   
    4 STATISTIC# NAME                                                                  CLASS      VALUE    STAT_ID
    5 ---------- ---------------------------------------------------------------- ---------- ---------- ----------
    6        124 redo synch writes                                                         8     400189 1439995281
    7        125 redo synch time                                                           8     500899 4215815172
    8        175 redo writes                                                               2      77169 1948353376
    9        177 redo write time                                                           2      26830 3094453259

    摘自Jonathan Lewis在oracle-l里对redo log writes相关统计的描述
    http://www.freelists.org/post/oracle-l/Log-file-sync-spike,2
    The stats
    redo synch writes
    redo synch time
    appear for a session that sends a “sync” call to the log writer.

    The stats
    redo writes
    redo write time
    appear for LGWR and record the number and duration of writes.

    Over a period as long as fifteen minutes, it is possible for a change in timing (two jobs running concurrently instead of serially) to cause the redo synch times (and log file sync waits) to change dramatically while the redo write time doesn’t change at all.

    参考译文:
    统计项redo synch writes,redo synch time出现在用户会话发送”sync”调用到lgwr时.
    统计项redo writes,redo write time出现在lgwr进程中,记录lgwr写操作进行的次数以及为此耗费的时间.
    在15分钟左右的时间里,可能会出现redo synch times(有两个任务在同步运行而不是串行运行)统计项发生显著的变化,而同时redo write time却基本没有变化的情况.

  • 在linux shell下运行strace -T -c -p , 跟踪lgwr在这段时间的所有系统调用统计
  • 在用户session下运行do_loop procedure
  • 在sys 用户下再次得到上述四个统计参数的值, 并得到lgwr 在此段时间的跟踪统计

    单进程测试的相关结果请参考下图:

    从图中, 我们可以看到, 使用nowait之后, io_submit以及semctl 的系统调用大大降低, 相应的调用时间也大大降低. 也就是处理能力大大提高. 另外, 不知诸位读者有没有发现, 在单进程运行的情况下, batch,wait模式比immediate,wait模式的效率还要低. 我个人的理解是, commit提交以后, 客户端需要等待lgwr完成的通知(semctl 调用即是负责此通知的系统调用), 但是因为它是batch模式, 也就是它会等待一段时间的其他事务的日志信息, 所以总体的响应时间反而更久. 其实这也是我进行后续的并发测试的主要动因, 检查batch,wait模式在并发状况下的表现.

    2. 使用并发进程测试batch,wait|nowait 的效果. 过程与上面单进程的类似. 只是运行用户session的操作改成使用脚本批量处理.

    view source print?
    01 ### 在脚本中执行上面的存储过程.
    02 [oracle@dbmain ~]$ cat bbb.sh
    03 #!/bin/sh
    04 . ~/.bash_profile
    05 sqlplus /nolog <<_END_
    06 conn james/james
    07 exec do_loop('batch,wait');
    08 _END_
    09   
    10 ### 同时运行19份不同的bbb.sh脚本, 也就是同时启动19个并发来进行插入
    11 [oracle@dbmain ~]$ cat ccc.sh
    12 nohup time ./bbb.sh &
    13 nohup time ./bbb.sh &
    14 nohup time ./bbb.sh &
    15 nohup time ./bbb.sh &
    16 nohup time ./bbb.sh &
    17 nohup time ./bbb.sh &
    18 nohup time ./bbb.sh &
    19 nohup time ./bbb.sh &
    20 nohup time ./bbb.sh &
    21 nohup time ./bbb.sh &
    22 nohup time ./bbb.sh &
    23 nohup time ./bbb.sh &
    24 nohup time ./bbb.sh &
    25 nohup time ./bbb.sh &
    26 nohup time ./bbb.sh &
    27 nohup time ./bbb.sh &
    28 nohup time ./bbb.sh &
    29 nohup time ./bbb.sh &

    并发测试的相关统计信息, 请参考下图:

    从上图可以看出, 虽然并发数达到19个, 总体的提交次数达到190,000次,但是实际发生的io_submit次数在batch,wait模式下也只是达到11427次, 而不是如同前面的单进程模式那样, io_submit的次数基本与commit次数一致, 也就是很多不同并发进程的事务都被组合到一次io_submit中了, 也就是batch了..

  • 相关内容