Append與Direct-Path Insert(一)

realkid4發表於2010-12-12

Append提示在進行批次資料載入的時候,是很高效的一個手段。前段進行實驗,發現這個hint在使用的時候,還是有一些需要注意的地方的。

 

1、  基礎知識

還是先看看基礎知識吧。append是oracle提供給SQL開發者使用的hint之一。所謂hint的作用,簡單說就是影響Oracle最佳化器生成特定的執行計劃。SQL是一種資料描述語言。實際對資料進行操作是透過對SQL的解析,生成執行計劃的過程。

 

Append提示的作用提示最佳化器使用直接路徑插入(Direct-Path Insert)方式。下面簡單說下Oracle的兩種插入和兩種模式。

 

兩種工作模式,分別成為序列化模式(Serial Mode)和並行模式(Parallel Mode)。序列化模式就是我們通常的SQL工作模式,一個SQL操作內部順序執行。並行模式就會將工作按照一定規則劃分為多個工作執行緒同時執行,之後匯合結果。在進行大資料量操作的時候,可以考慮採用並行模式以提高效率。

 

插入方式上,Oralce有Conventional Insert和Direct-Path Insert兩種。

 

傳統插入(Conventional Insert)方式是我們通常使用的Insert方式。預設情況下,傳統插入是使用序列化模式的。在序列化模式下,如果在insert中使用append提示,Oracle會對該條insert使用Direct-Path插入方式。

 

直接路徑(Direct-Path)方式在是並行操作模式下的預設插入方式。在並行模式下,只有使用noappend提示,才能使用傳統的插入方式。

 

所以,插入操作的時候,究竟是使用並行還是序列模式,與append提示無關。

 

在使用直接路徑插入的時候,資料並不是先從資料表中找到隨機的空位後插入,而是直接將資料插入到資料表的末尾,直接推高高水位線。這種操作方式,較傳統的方式要快。

 

2、  快速的直接路徑插入

相對於傳統模式,使用append提示之後,批次插入的速度可以得到顯著的提升。

 

SQL> create table t as select * from dba_objects where 1=0;

Table created

 

SQL> select count(*) from t;

 

  COUNT(*)

----------

         0

 

Executed in 0.01 seconds

 

//一般插入方式

SQL> insert into t select * from dba_objects;

 

50367 rows inserted

 

Executed in 0.571 seconds

//還原環境,刪除資料

SQL> truncate table t;

 

Table truncated

 

Executed in 0.431 seconds

//Append插入方式

SQL> insert /*+ append */into t select * from dba_objects;

 

50367 rows inserted

 

Executed in 0.45 seconds

 

透過上面的簡單實驗,可以看到:在相同的條件下,使用append進行資料表的插入,要顯著的快於傳統方式。這種特點在海量資料載入、遷移的過程中,可以顯著的提高效率。要注意,這種方式因為直接提升高水位線,不會利用表中原有被刪除的空間,在一些刪除操作比較多的表中,會造成很大的空間浪費,要避免濫用引發空間膨脹問題。

 

3、  Append操作與鎖

 

在使用Append操作時,要注意Append操作是一種特殊的插入操作,會阻止包括自身會話在內的插入操作。首先,我們先看一下普通插入的系統行為。

 

//資料準備,建立資料表

SQL> create table t as select owner, object_id, rownum num from dba_objects where 1=0;

 

Table created

 

//session1中,啟動一個插入操作

SQL> insert into t select owner,object_id,rownum from dba_objects where rownum<10;

 

9 rows inserted

 

SQL> select * from v$mystat where rownum<2;

 

       SID STATISTIC#      VALUE

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

       140          0          1

 

//觀察系統中鎖的情況

 

SQL> select * from v$lock where sid=140;

 

ADDR     KADDR           SID TYPE        ID1        ID2      LMODE    REQUEST

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

6BDC4074 6BDC408C        140 TM        53083          0          3          0 

6BE3BE7C 6BE3BF98        140 TX       458760        692          6          0

 

說明:Session1(SID=140)首先在資料表物件加上一個TM鎖,模式為3共享鎖,同時對其插入的資料項加了事務TX鎖,模式為6獨佔鎖;

 

//啟動另一個session2,執行插入操作

SQL> insert into t select owner, object_id, rownum from dba_objects where object_id=120;

 

1 row inserted

 

SQL> select sid from v$mystat where rownum<2;

 

       SID

----------

142

 

SQL> select * from v$lock where sid in (140,142);

 

ADDR     KADDR           SID TYPE        ID1        ID2      LMODE    REQUEST

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

6BDC4074 6BDC408C        140 TM        53083          0          3          0

6BDC4138 6BDC4150        142 TM        53083          0          3          0

6BE127B8 6BE128D4        142 TX       655371        658          6          0 

6BE3BE7C 6BE3BF98        140 TX       458760        692          6          0 

 

說明:session2(SID=142)進行插入操作,也嘗試在物件表(ID1 53083)上加一個共享鎖TM,LMODE=3。與Session1(SID=140)的TM鎖可以共存(共享鎖之間),同時將插入事務鎖TX,型別為獨佔鎖,與原有的session1獨佔鎖加上的是不一樣的物件。

 

 

說明:在傳統的插入操作中,會話之間插入操作是不會相互影響的。同一個事務內部,進行插入操作之間,也沒有互斥的情況。

 

下面我們開始實驗append提示。(下面實驗是使用相同的兩個session(sid=140,142))。

//在session1(sid=140)中使用append關鍵字插入。

SQL> insert /*+ append */ into t select owner, object_id, rownum from dba_objects where rownum<10;

 

9 rows inserted

 

//同一個session內,進行相同的操作

SQL> insert /*+ append */ into t select owner, object_id, rownum from dba_objects where rownum<10;

 

ORA-12838: 無法在並行模式下修改之後讀/修改物件

 

說明:即使在同一個session裡,對一個表進行append插入之後,必須先commit之後,才能進行相同表的操作。這個特點可能與append採用的並行模式操作有關。

 

這個時候,我們檢視一下當前sesson1(sid=140)的鎖狀態。

 

//檢視session1的鎖狀況

SQL> select * from v$lock where sid=140;

 

ADDR     KADDR           SID TYPE        ID1        ID2      LMODE    REQUEST 

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

6BDC4074 6BDC408C        140 TM        53083          0          6          0 

6BE3BE7C 6BE3BF98        140 TX       196611        652          6          0

 

請注意:當我們進行append操作的時候,oracle對錶t加鎖狀況出現差異。變化點在資料表鎖TM的模式LMODE為6(排他鎖),這點與一般插入存在差異。TM模式6的現象說明,在進行append操作的時候,oracle對資料表級的鎖操作不是一個共享鎖(LMODE=3),而是一個排他鎖(LMODE=6)。從鎖知識角度,如果這時其他會話嘗試插入操作(向資料表T加一個LMODE=3的共享鎖),是會被hange住。

 

//此時session2(sid=142)

SQL> select sid from v$mystat where rownum<2;

 

       SID

----------

       142

//嘗試以一個一般方式插入資料

SQL> insert into t select owner, object_id, rownum from dba_objects where object_id=120;

//session2被hange住

 

//此時檢視兩個會話的鎖狀況

SQL> select * from v$lock where sid in (140,142);

 

ADDR     KADDR           SID TYPE        ID1        ID2      LMODE    REQUEST 

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

6BDC4074 6BDC408C        140 TM        53083          0          6          0 

6BDC4138 6BDC4150        142 TM        53083          0          0          3 

6BE3BE7C 6BE3BF98        140 TX       196611        652          6          0 

 

說明:此時session2嘗試在表T上加入一個共享鎖Lmode=3,因為已經被sid=140獨佔排他。所以被hange住。

 

//session1 commit之後。

SQL> commit;

 

Commit complete

 

//session2解除hange狀態

 

結論:實驗結果和我們預想的一樣。共享鎖和排他鎖是不能相容的。通常,當我們在一個會話中使用了append一批資料後,其他會話是不能插入操作。如果進行的話,會被hange住。進行append操作的會話,也不能在表中進行其他操作。除非提交或回滾事務。

 

作為這個結論的擴充實驗,我們進行下列嘗試。

 

//在session1已經進行append的操作情況下

//如果session2也要求進行append的操作時候

 

SQL> insert /*+ append */ into t select owner, object_id, rownum from dba_objects where rownum<10;

//session2也hange住

 

SQL> select * from v$lock where sid in (140,142);

 

ADDR     KADDR           SID TYPE        ID1        ID2      LMODE    REQUEST

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

6BDC4074 6BDC408C        140 TM        53083          0          6          0

6BDC4138 6BDC4150        142 TM        53083          0          0          6

6BE3BE7C 6BE3BF98        140 TX        65571        638          6          0

 

//sesion2 被hange住

 

結論一致,區別在於session2在進行append的操作的時候,也會嘗試獲取排他的表鎖,自然也會被hange住。

 

綜上所述:append操作是一種有並行操作趨勢的快速插入hint。在使用的時候,要注意及時的commit,以防止出現嚴重的併發操作問題。同時,在高併發的系統(OLTP)中,也要注意慎用。

 

 

那麼,是不是append都是要獲取排他鎖呢?排他鎖和共享鎖之間關係如何協同?還有在同一個會話中,對鎖控制有什麼特點?我們在下一篇中繼續探討。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/17203031/viewspace-681591/,如需轉載,請註明出處,否則將追究法律責任。

相關文章