Oracle 10gR2+ 的异步提交
Oracle 10gR2开始, 增强了提交的功能, 实现异步/批量的提交
1. 异步提交
默认时提交的步骤:
1) 向系统全局区(SGA)中的重做缓冲区(redo log buffer)中写'事务结束(end of transaction)'记录
2) 通知(发送消息到)写日志进程(LGWR), 告诉它刷新重做缓冲区到磁盘
3) 等待磁盘刷新完成. 这就是常见的'日志文件同步'('log file sync')事件
10gR2 COMMIT增加了新选项:
WAIT: 等待相应的重做信息写到在线重做日志文件中后,提交命令才返回(缺省)
NOWAIT: 不等重做信息写到日志中, 提交命令就返回
IMMEDIATE: 写日志进程立刻写重做信息(缺省). 即强制执行一次磁盘 IO.
BATCH: 将重做信息缓冲起来. 写日志进程到时再写重做信息.
虽然提高了性能, 但是一旦系统宕机, 缓冲区中的已提交事务的重做信息将丢失. 如果遭遇磁盘IO错误, 也会丢失重做信息.
2. COMMIT命令
新的 COMMIT 命令增加了如下选项:
COMMIT WRITE IMMEDIATE|BATCH WAIT|NOWAIT;
见WRITE Clause
COMMIT WRITE NOWAIT: 不做前面提到的第3步
COMMIT WRITE BATCH,NOWAIT: 不做第2和3步, 重做记录保留在缓冲区内, 直到其他人提交导致刷新, 或后台事件导致缓冲区异步的刷新
后台事件如下:
缓冲区1/3满
缓冲区充满1M
每3秒
测试:
CREATE TABLE commit_test (
id NUMBER(10),
description VARCHAR2(50),
CONSTRAINT commit_test_pk PRIMARY KEY (id)
);
CONN A/A
SET SERVEROUTPUT ON
DECLARE
function get_waits(p_event in varchar2) return number
is
l_waits NUMBER;
begin
select total_waits
into l_waits
from v$session_event
where event = p_event
and sid = (select sid from v$mystat where rownum=1);
return l_waits;
exception
when no_data_found then return 0;
end;
PROCEDURE do_loop (p_type IN VARCHAR2) AS
l_start NUMBER;
l_loops NUMBER := 1000;
l_lfs NUMBER;
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE commit_test';
l_lfs := get_waits('log file sync');
l_start := DBMS_UTILITY.get_time;
FOR i IN 1 .. l_loops LOOP
INSERT INTO commit_test (id, description)
VALUES (i, 'Description for ' || i);
CASE p_type
WHEN ' ' THEN COMMIT;
WHEN 'WRITE' THEN COMMIT WRITE;
WHEN 'WRITE WAIT' THEN COMMIT WRITE WAIT;
WHEN 'WRITE NOWAIT' THEN COMMIT WRITE NOWAIT;
WHEN 'WRITE BATCH' THEN COMMIT WRITE BATCH;
WHEN 'WRITE IMMEDIATE' THEN COMMIT WRITE IMMEDIATE;
WHEN 'WRITE BATCH WAIT' THEN COMMIT WRITE BATCH WAIT;
WHEN 'WRITE BATCH NOWAIT' THEN COMMIT WRITE BATCH NOWAIT;
WHEN 'WRITE IMMEDIATE WAIT' THEN COMMIT WRITE IMMEDIATE WAIT;
WHEN 'WRITE IMMEDIATE NOWAIT' THEN COMMIT WRITE IMMEDIATE NOWAIT;
END CASE;
END LOOP;
DBMS_OUTPUT.put_line(RPAD('COMMIT ' || p_type, 30)
|| ': ' || (DBMS_UTILITY.get_time - l_start)
|| ': ' || (get_waits('log file sync') - l_lfs)
);
END;
BEGIN
do_loop(' ');
do_loop('WRITE');
do_loop('WRITE WAIT');
do_loop('WRITE NOWAIT');
do_loop('WRITE BATCH');
do_loop('WRITE IMMEDIATE');
do_loop('WRITE BATCH WAIT');
do_loop('WRITE BATCH NOWAIT');
do_loop('WRITE IMMEDIATE WAIT');
do_loop('WRITE IMMEDIATE NOWAIT');
END;
/
COMMIT : 19: 0 COMMIT WRITE : 151: 1000 COMMIT WRITE WAIT : 150: 1000 COMMIT WRITE NOWAIT : 20: 0 COMMIT WRITE BATCH : 151: 1000 COMMIT WRITE IMMEDIATE : 152: 1000 COMMIT WRITE BATCH WAIT : 151: 1000 COMMIT WRITE BATCH NOWAIT : 15: 0 COMMIT WRITE IMMEDIATE WAIT : 153: 1000 COMMIT WRITE IMMEDIATE NOWAIT : 20: 0可以看出
1) COMMIT什么参数都不带, 等于NOWAIT, 原因后面讲
2) 参数默认是IMMEDIATE, WAIT
3) BATCH和IMMEDIATE速度差不多(为啥?)
4) WAIT产生等待事件, NOWAIT不产生
5) BATCH+NOWAIT最快, IMMEDIATE+WAIT最慢
3. 系统初始化参数
新增的系统参数是 COMMIT_WRITE
语法: COMMIT_WRITE = '{IMMEDIATE | BATCH},{WAIT |NOWAIT}'
可以在系统级或会话级设置, ALTER SYSTEM, ALTER SESSION
比如:
SQL> show parameter commit NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ commit_point_strength integer 1 commit_write string max_commit_propagation_delay integer 0 SQL> alter system set commit_write='batch,nowait'; System altered. SQL> show parameter commit NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ commit_point_strength integer 1 commit_write string batch,nowait max_commit_propagation_delay integer 0 SQL>
11g取消了COMMIT_WRITE (为了兼容仍保留), 拆分为2个单独的参数 COMMIT_LOGGING 和 COMMIT_WAIT, 分别对应 IMMEDIATE | BATCH 和 WAIT | NOWAIT
测试:
conn a/a
SET SERVEROUTPUT ON
DECLARE
function get_waits(p_event in varchar2) return number
is
l_waits NUMBER;
begin
select total_waits
into l_waits
from v$session_event
where event = p_event
and sid = (select sid from v$mystat where rownum=1);
return l_waits;
exception
when no_data_found then return 0;
end;
PROCEDURE do_loop (p_type IN VARCHAR2) AS
l_start NUMBER;
l_loops NUMBER := 1000;
l_lfs NUMBER;
BEGIN
if p_type is not null then
EXECUTE IMMEDIATE 'ALTER SESSION SET COMMIT_WRITE=''' || p_type || '''';
end if;
EXECUTE IMMEDIATE 'TRUNCATE TABLE commit_test';
l_lfs := get_waits('log file sync');
l_start := DBMS_UTILITY.get_time;
FOR i IN 1 .. l_loops LOOP
INSERT INTO commit_test (id, description)
VALUES (i, 'Description for ' || i);
COMMIT;
END LOOP;
DBMS_OUTPUT.put_line(RPAD('COMMIT_WRITE=' || p_type, 30)
|| ': ' || (DBMS_UTILITY.get_time - l_start)
|| ': ' || (get_waits('log file sync') - l_lfs)
);
END;
BEGIN
do_loop(NULL);
do_loop('WAIT');
do_loop('NOWAIT');
do_loop('BATCH');
do_loop('IMMEDIATE');
do_loop('BATCH,WAIT');
do_loop('BATCH,NOWAIT');
do_loop('IMMEDIATE,WAIT');
do_loop('IMMEDIATE,NOWAIT');
END;
/
COMMIT_WRITE= : 20: 0 COMMIT_WRITE=WAIT : 151: 1000 COMMIT_WRITE=NOWAIT : 19: 0 COMMIT_WRITE=BATCH : 14: 0 COMMIT_WRITE=IMMEDIATE : 19: 0 COMMIT_WRITE=BATCH,WAIT : 150: 1000 COMMIT_WRITE=BATCH,NOWAIT : 15: 0 COMMIT_WRITE=IMMEDIATE,WAIT : 153: 1000 COMMIT_WRITE=IMMEDIATE,NOWAIT : 20: 0第因为在第3步设置了NOWAIT, 所以后面第4,5步也继承了这个配置
4. PLSQL中的优化
PL/SQL 会自动将其中的 COMMIT 优化成为"COMMIT WRITE NOWAIT", 只有最后一次 COMMIT 才是真正的"COMMIT"
conn a/a
set serveroutput on size unlimited
truncate table commit_test;
select total_waits
from v$session_event
where event = 'log file sync'
and sid = (select sid from v$mystat where rownum=1);
declare
l_loops number := 1000;
begin
FOR i IN 1 .. l_loops LOOP
INSERT INTO commit_test (id, description)
VALUES (i, 'Description for ' || i);
COMMIT;
END LOOP;
end;
/
select total_waits
from v$session_event
where event = 'log file sync'
and sid = (select sid from v$mystat where rownum=1);
TOTAL_WAITS
-----------
1
SQL> 2 3 4 5 6 7 8 9 10
PL/SQL procedure successfully completed.
SQL> 2 3 4
TOTAL_WAITS
-----------
2
只产生了1次等待事件10gR2版本以前也发现有异步提交, 见The LGWR dilemma
外部链接:
Commit Enhancements in Oracle 10g Database Release 2
10gR2 New Feature: Asynchronous Commit
On setting commit_write
Quantifying Commit Time
Asynchronous Commit - New Feature in Oracle 10GR2 (10.2)
Expert Oracle Database 11g Administration By Sam R. Alapati
-fin-

No comments:
Post a Comment