2012-07-12 69 views
7

你能幫我理解這句話嗎?Oracle:Bulk收集性能

沒有批量綁定,PL/SQL發送SQL語句的SQL引擎 爲每個插入,更新或刪除導致傷害的性能 上下文切換記錄。

回答

17

在Oracle中,有一個SQL虛擬機(VM)和一個PL/SQL VM。當您需要從一個虛擬機遷移到另一個虛擬機時,會產生上下文切換的代價。單獨來說,這些上下文轉換相對較快,但是當您逐行處理時,它們可以佔用代碼花費的很大一部分時間。當您使用批量綁定時,您可以將多行數據從一個虛擬機移動到另一個虛擬機,只需一次上下文切換,顯着減少上下文切換次數,使代碼更快。

以一個明確的遊標爲例。如果我寫這樣的事情

DECLARE 
    CURSOR c 
     IS SELECT * 
      FROM source_table; 
    l_rec source_table%rowtype; 
BEGIN 
    OPEN c; 
    LOOP 
    FETCH c INTO l_rec; 
    EXIT WHEN c%notfound; 

    INSERT INTO dest_table(col1, col2, ... , colN) 
     VALUES(l_rec.col1, l_rec.col2, ... , l_rec.colN); 
    END LOOP; 
END; 

然後我每次執行取,我

  • 執行從PL/SQL VM到SQL VM
  • 上下文轉移問計於SQL VM執行光標以生成下一行數據
  • 執行從SQL VM到PL/SQL VM的另一個上下文轉換以返回我的單行數據

每當我插入一行時,我都會做同樣的事情。我承擔了將PL/SQL VM中的一行數據發送到SQL VM的上下文轉換成本,要求SQL執行INSERT語句,然後將其他上下文轉換回PL/SQL。

如果source_table有100萬行,那就是400萬的上下文轉換,這可能會佔我的代碼耗費時間的合理部分。另一方面,如果我執行的BULK COLLECTLIMIT爲100,那麼我可以通過從SQL VM檢索100行數據到PL/SQL中的集合中來減少99%的上下文轉換次數,每當我在那裏發生上下文轉換時,上下文轉換並將100行插入到目標表中。

如果可以重寫我的代碼使用批量操作

DECLARE 
    CURSOR c 
     IS SELECT * 
      FROM source_table; 
    TYPE nt_type IS TABLE OF source_table%rowtype; 
    l_arr nt_type; 
BEGIN 
    OPEN c; 
    LOOP 
    FETCH c BULK COLLECT INTO l_arr LIMIT 100; 
    EXIT WHEN l_arr.count = 0; 

    FORALL i IN 1 .. l_arr.count 
     INSERT INTO dest_table(col1, col2, ... , colN) 
     VALUES(l_arr(i).col1, l_arr(i).col2, ... , l_arr(i).colN); 
    END LOOP; 
END; 

的現在,我每次執行提取時間,我取回100行數據到我的收藏與一組情境的變化的。每當我做我的FORALL插入,我插入100行與一組上下文轉移。如果source_table有100萬行,這意味着我已經從400萬上下文轉換到40,000上下文轉換。如果上下文的變化佔據了我代碼已用時間的20%,我已經消除了19.8%的流逝時間。

您可以增加LIMIT的大小以進一步減少上下文轉換次數,但您很快就會遇到收益遞減規律。如果您使用1000而不是100的LIMIT,那麼您將消除99.9%的上下文偏移,而不是99%。這意味着您的收藏使用了10倍以上的PGA內存。在我們的假設例子中,它只會消除0.18%的時間。通過消除額外的上下文偏移,您很快就會達到您正在使用的額外內存所增加的時間超過您節省的時間。一般而言,100到1000之間的某個LIMIT很可能是最佳選擇。

當然,在這個例子中,這將是更有效的還是要消除所有方面的變化,並在單個SQL語句

INSERT INTO dest_table(col1, col2, ... , colN) 
    SELECT col1, col2, ... , colN 
    FROM source_table; 

它只會意義求助於在PL/SQL做的一切如果你正在對源表中的數據進行某種操作,而這些操作無法在SQL中合理實現,那麼首先要考慮這一點。

此外,我在示例中故意使用了顯式遊標。如果您使用隱式遊標,則在最新版本的Oracle中,隱式地獲得BULK COLLECTLIMIT的好處。還有另一個StackOverflow問題,討論相關的performance benefits of implicit and explicit cursors with bulk operations,這些問題涉及更多有關這些特定皺紋的細節。

1

據我所知,有兩個引擎,PL/SQL engine and SQL Engine。執行查詢,使在一個時間使用一個發動機的比兩個

實施例之間的切換更有效:

INSERT INTO t VALUES(1) 

由SQL發動機而

FOR Lcntr IN 1..20 

    END LOOP 

由PL執行處理的/ SQL引擎

如果將上述兩個語句組合在一起,將INSERT放入循環中,

FOR Lcntr IN 1..20 
    INSERT INTO t VALUES(1) 
END LOOP 

對於每次(20)次迭代,Oracle將在兩個引擎之間切換。 在這種情況下,建議使用PL/SQL引擎執行BULK INSERT。

+0

你最後一句話是有點欺騙。 BULK使上下文切換僅發生一次,儘管它仍然發生。 – viper 2015-03-02 15:44:16