1. 程式人生 > >Insert語句加/*+APPEND */在迴圈中單條提交對系統的影響分析

Insert語句加/*+APPEND */在迴圈中單條提交對系統的影響分析

1、/*+APPEND */提示的用途

/*+APPEND */提示,是一個INSERT語句專有的hint,它的作用,大家都知道是用來提升insert速度的,並且效果非常的明顯,至於它的提升速度的工作原理,在筆者的另一篇博文《用直接路徑(direct-path)insert提升效能的兩種方法》中有提到(以優點方式提出),該文地址為:http://blog.csdn.net/ljunjie82/article/details/42615233

2、單條迴圈提交中使用/*+APPEND */的巨大影響

再好的東西,有好的一面,也有不好的一面,用得好,可以助你事半功倍,用不好,將會帶來巨大的影響。

/*+APPEND */由於期是在高水位以上插入,以及/*+APPEND */會給表加6級排它鎖的特性,所以試想,如果要在loop或if迴圈中,要迴圈的插入一百萬行資料,每迴圈一次只有一行符合條件的資料插入,commit只能放在迴圈之內(/*+APPEND */決定著commit無法放在特環外面),即代表著一萬行資料,有一千萬次commit。

這種使用場景,筆者已經在多個專案中看到,所以在此將該種用法的影響分析出來供有需要的人士參考。

這樣的操作,對ORACLE資料庫將會帶來怎樣的嚴重後果?

3、影響分析測試

3.1 loop迴圈中使用/*+APPEND */ hint的INSERT單條提交場景

(1)建立三張測試表

create table emp(empno number);             -- 遊標值引用表

create table emp_inter(numberno number);    --中間表

create table emp_append_test(empno number); --目標表

(2)向遊標值引用表與中間表各插入10000行資料

set timing on;

declare

i number:=1;

begin

loop

insert into emp (empno) values (i);

insert into emp_inter (numberno) values (i);

commit;

i:=i+1;

exit when i=10001;

end loop;

end;

/

輸出時間值:Elapsed: 00:00:02.8    --同時向兩張表insert 10000行資料,耗時2.8秒

3.2 對空間佔用的嚴重影響測試與分析

3.2.1對三張表所佔用空間進行測試前記錄

select 'EMP' as table_name,

count(distinct dbms_rowid.rowid_block_number(rowid)) as blocks from EMP

 union all

select 'EMP_INTER' as table_name,count(distinct dbms_rowid.rowid_block_number(rowid)) as blocks from EMP_INTER

 union all

select 'EMP_APPEND_TEST' as table_name,count(distinct dbms_rowid.rowid_block_number(rowid)) as blocks from EMP_APPEND_TEST

輸出結果如下:

  table_name

blocks

EMP

16

EMP_INTER

16

EMP_APPEND_TEST

0

    在此已經看到,EMP、EMP_INTER兩張表,各插入999行資料,佔用block為16個,當前沒有插入資料的EMP_APPEND_TEST表佔用0個block。

3.2.2在loop迴圈中加/*+APPEND */hint做insert資料

向目標表emp_append_test插入資料

set serveroutput on

set timing on

declare

n number:=1;

begin

  for c in (select empno from emp)

loop

insert /*+APPEND */ into emp_append_test select * from emp_inter where numberno=c.empno;

n:=n+1;

commit;

end loop;

dbms_output.put_line('insert rows is :'||n);

end;

/

輸出值:insert rows is :10000     --插入10000行資料

Elapsed: 00:00:11.62      --此次向一張表中插入10000行資料,耗時11.62秒

3.2.3 再次查詢三張表佔用的block數量

select 'EMP' as table_name,count(distinct dbms_rowid.rowid_block_number(rowid)) as blocks from EMP

 union all

select 'EMP_INTER' as table_name,count(distinct dbms_rowid.rowid_block_number(rowid)) as blocks from EMP_INTER

 union all

select 'EMP_APPEND_TEST' as table_name,count(distinct dbms_rowid.rowid_block_number(rowid)) as blocks from EMP_APPEND_TEST;

輸出結果如下:

      table_name

blocks

EMP

16

EMP_INTER

16

EMP_APPEND_TEST

10000

從上面看來,結果是非常可怕的,插入一萬行資料,佔用一萬個block,以每個block 8KB計算,一萬行資料佔用78.1MB左右(10000*8/1024)。

3.3 對查詢效能影響

(1)對未使用/*+APPEND */迴圈單條commit的表EMP查詢效能測試

SQL> set autotrace on statistics

SQL> select * from emp where empno =1;

-------------

Statistics

----------------------------------------------------------

          1  recursive calls

          0  db block gets

         23  consistent gets

          0  physical reads

          0  redo size

        523  bytes sent via SQL*Net to client

        523  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

          1  rows processed

(2)對使用/*+APPEND */迴圈單條commit的表EMP_APPEND_TEST查詢效能測試

SQL> set autotrace on statistics

SQL> select * from EMP_APPEND_TEST where empno = 1;

---------------------------

Statistics

----------------------------------------------------------

          1  recursive calls

          0  db block gets

      20003consistent gets

      10000  physical reads

          0  redo size

        523  bytes sent via SQL*Net to client

        523  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

          1  rows processed

(3)查詢效能結果比較:

table_name

consistent gets

physical reads

EMP

23

21

EMP_APPEND_TEST

20003

10024

consistent gets翻了869倍

physical reads翻了477倍

4、問題小結

    Insert語句加/*+APPEND*/ hint在迴圈中單條提交,由於/*+APPEND */ hint是在高水位線以上插入的特性,導致每提交一次,就會取一個新的block存放,高水位就上推一個block,以及/*+APPEND */ hint會給表加6級排它鎖的特導,導致必須在commit後才能插入新的資料,大量單條/*+APPEND */插入,使得表急劇增大,除對insert本身造成效能影響之外,對以後的select、update、delete更是造成更巨大的影響。

本文作者:黎俊傑(網名:踩點),從事”系統架構、作業系統、儲存裝置、資料庫、中介軟體、應用程式“六個層面系統性的效能優化工作

歡迎加入 系統性能優化專業群,共同探討效能優化技術。群號:258187244