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, 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